Add autoOrient operation and constructor option #4144
@ -46,6 +46,7 @@ and https://www.cairographics.org/operators/
|
|||||||
| [images[].input.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. |
|
| [images[].input.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. |
|
||||||
| [images[].input.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`. |
|
| [images[].input.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`. |
|
||||||
| [images[].input.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. |
|
| [images[].input.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. |
|
||||||
|
| [images[].autoOrient] | <code>Boolean</code> | <code>false</code> | set to true to use EXIF orientation data, if present, to orient the image. |
|
||||||
| [images[].blend] | <code>String</code> | <code>'over'</code> | how to blend this image with the image below. |
|
| [images[].blend] | <code>String</code> | <code>'over'</code> | how to blend this image with the image below. |
|
||||||
| [images[].gravity] | <code>String</code> | <code>'centre'</code> | gravity at which to place the overlay. |
|
| [images[].gravity] | <code>String</code> | <code>'centre'</code> | gravity at which to place the overlay. |
|
||||||
| [images[].top] | <code>Number</code> | | the pixel offset from the top edge. |
|
| [images[].top] | <code>Number</code> | | the pixel offset from the top edge. |
|
||||||
|
@ -33,6 +33,7 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
|
|||||||
| [options.failOn] | <code>string</code> | <code>"'warning'"</code> | When to abort processing of invalid pixel data, one of (in order of sensitivity, least to most): 'none', 'truncated', 'error', 'warning'. Higher levels imply lower levels. Invalid metadata will always abort. |
|
| [options.failOn] | <code>string</code> | <code>"'warning'"</code> | When to abort processing of invalid pixel data, one of (in order of sensitivity, least to most): 'none', 'truncated', 'error', 'warning'. Higher levels imply lower levels. Invalid metadata will always abort. |
|
||||||
| [options.limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | Do not process input images where the number of pixels (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). |
|
| [options.limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | Do not process input images where the number of pixels (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted. An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). |
|
||||||
| [options.unlimited] | <code>boolean</code> | <code>false</code> | Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF). |
|
| [options.unlimited] | <code>boolean</code> | <code>false</code> | Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF). |
|
||||||
|
| [options.autoOrient] | <code>boolean</code> | <code>false</code> | Set this to `true` to rotate/flip the image to match EXIF `Orientation`, if any. |
|
||||||
| [options.sequentialRead] | <code>boolean</code> | <code>true</code> | Set this to `false` to use random access rather than sequential read. Some operations will do this automatically. |
|
| [options.sequentialRead] | <code>boolean</code> | <code>true</code> | Set this to `false` to use random access rather than sequential read. Some operations will do this automatically. |
|
||||||
| [options.density] | <code>number</code> | <code>72</code> | number representing the DPI for vector images in the range 1 to 100000. |
|
| [options.density] | <code>number</code> | <code>72</code> | number representing the DPI for vector images in the range 1 to 100000. |
|
||||||
| [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. |
|
| [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. |
|
||||||
|
@ -72,15 +72,9 @@ image
|
|||||||
```
|
```
|
||||||
**Example**
|
**Example**
|
||||||
```js
|
```js
|
||||||
// Based on EXIF rotation metadata, get the right-side-up width and height:
|
// Get dimensions taking EXIF Orientation into account.
|
||||||
|
const { autoOrient } = await sharp(input).metadata();
|
||||||
const size = getNormalSize(await sharp(input).metadata());
|
const { width, height } = autoOrient;
|
||||||
|
|
||||||
function getNormalSize({ width, height, orientation }) {
|
|
||||||
return (orientation || 0) >= 5
|
|
||||||
? { width: height, height: width }
|
|
||||||
: { width, height };
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,22 +1,19 @@
|
|||||||
## rotate
|
## rotate
|
||||||
> rotate([angle], [options]) ⇒ <code>Sharp</code>
|
> rotate([angle], [options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Rotate the output image by either an explicit angle
|
Rotate the output image.
|
||||||
or auto-orient based on the EXIF `Orientation` tag.
|
|
||||||
|
|
||||||
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.
|
For example, `-450` will produce a 270 degree rotation.
|
||||||
|
|
||||||
When rotating by an angle other than a multiple of 90,
|
When rotating by an angle other than a multiple of 90,
|
||||||
the background colour can be provided with the `background` option.
|
the background colour can be provided with the `background` option.
|
||||||
|
|
||||||
If no angle is provided, it is determined from the EXIF data.
|
For backwards compatibility, if no angle is provided, `.autoOrient()` will be called.
|
||||||
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.
|
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
|
||||||
Only one rotation can occur per pipeline.
|
pipeline will be ignored.
|
||||||
Previous calls to `rotate` in the same pipeline will be ignored.
|
|
||||||
|
|
||||||
Multi-page images can only be rotated by 180 degrees.
|
Multi-page images can only be rotated by 180 degrees.
|
||||||
|
|
||||||
@ -35,18 +32,6 @@ for example `.rotate(x).extract(y)` will produce a different result to `.extract
|
|||||||
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
|
| [options] | <code>Object</code> | | if present, is an Object with optional attributes. |
|
||||||
| [options.background] | <code>string</code> \| <code>Object</code> | <code>"\"#000000\""</code> | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
| [options.background] | <code>string</code> \| <code>Object</code> | <code>"\"#000000\""</code> | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
||||||
|
|
||||||
**Example**
|
|
||||||
```js
|
|
||||||
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**
|
**Example**
|
||||||
```js
|
```js
|
||||||
const rotateThenResize = await sharp(input)
|
const rotateThenResize = await sharp(input)
|
||||||
@ -60,6 +45,34 @@ const resizeThenRotate = await sharp(input)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## autoOrient
|
||||||
|
> autoOrient() ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
|
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**
|
||||||
|
```js
|
||||||
|
const output = await sharp(input).autoOrient().toBuffer();
|
||||||
|
```
|
||||||
|
**Example**
|
||||||
|
```js
|
||||||
|
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);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## flip
|
## flip
|
||||||
> flip([flip]) ⇒ <code>Sharp</code>
|
> flip([flip]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
|
@ -16,6 +16,10 @@ Requires libvips v8.16.0
|
|||||||
|
|
||||||
* Expose WebP `smartDeblock` output option.
|
* Expose WebP `smartDeblock` output option.
|
||||||
|
|
||||||
|
* Add `autoOrient` operation and constructor option.
|
||||||
|
[#4151](https://github.com/lovell/sharp/pull/4151)
|
||||||
|
[@happycollision](https://github.com/happycollision)
|
||||||
|
|
||||||
* TypeScript: Ensure channel counts use the correct range.
|
* TypeScript: Ensure channel counts use the correct range.
|
||||||
[#4197](https://github.com/lovell/sharp/pull/4197)
|
[#4197](https://github.com/lovell/sharp/pull/4197)
|
||||||
[@DavidVaness](https://github.com/DavidVaness)
|
[@DavidVaness](https://github.com/DavidVaness)
|
||||||
|
@ -308,3 +308,6 @@ GitHub: https://github.com/sumitd2
|
|||||||
|
|
||||||
Name: Caleb Meredith
|
Name: Caleb Meredith
|
||||||
GitHub: https://github.com/calebmer
|
GitHub: https://github.com/calebmer
|
||||||
|
|
||||||
|
Name: Don Denton
|
||||||
|
GitHub: https://github.com/happycollision
|
||||||
|
@ -110,6 +110,7 @@ const blend = {
|
|||||||
* @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
|
* @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
|
||||||
* @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`.
|
* @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`.
|
||||||
* @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
|
* @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
|
||||||
|
* @param {Boolean} [images[].autoOrient=false] - set to true to use EXIF orientation data, if present, to orient the image.
|
||||||
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
|
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
|
||||||
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
|
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
|
||||||
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
||||||
|
@ -132,6 +132,7 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
||||||
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
||||||
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF).
|
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF).
|
||||||
|
* @param {boolean} [options.autoOrient=false] - Set this to `true` to rotate/flip the image to match EXIF `Orientation`, if any.
|
||||||
* @param {boolean} [options.sequentialRead=true] - Set this to `false` to use random access rather than sequential read. Some operations will do this automatically.
|
* @param {boolean} [options.sequentialRead=true] - Set this to `false` to use random access rather than sequential read. Some operations will do this automatically.
|
||||||
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
||||||
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
|
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
|
||||||
@ -194,7 +195,6 @@ const Sharp = function (input, options) {
|
|||||||
canvas: 'crop',
|
canvas: 'crop',
|
||||||
position: 0,
|
position: 0,
|
||||||
resizeBackground: [0, 0, 0, 255],
|
resizeBackground: [0, 0, 0, 255],
|
||||||
useExifOrientation: false,
|
|
||||||
angle: 0,
|
angle: 0,
|
||||||
rotationAngle: 0,
|
rotationAngle: 0,
|
||||||
rotationBackground: [0, 0, 0, 255],
|
rotationBackground: [0, 0, 0, 255],
|
||||||
|
82
lib/index.d.ts
vendored
@ -364,24 +364,72 @@ declare namespace sharp {
|
|||||||
//#region Operation functions
|
//#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.
|
||||||
*
|
*
|
||||||
* 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).
|
* Only one rotation can occur per pipeline (aside from an initial call without
|
||||||
* @param angle angle of rotation. (optional, default auto)
|
* arguments to orient via EXIF data). Previous calls to `rotate` in the same
|
||||||
* @param options if present, is an Object with optional attributes.
|
* 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
|
* @throws {Error} Invalid parameters
|
||||||
* @returns A sharp instance that can be used to chain operations
|
|
||||||
*/
|
*/
|
||||||
rotate(angle?: number, options?: RotateOptions): Sharp;
|
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.
|
* 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.
|
* The use of flip implies the removal of the EXIF Orientation tag, if any.
|
||||||
@ -898,6 +946,13 @@ declare namespace sharp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SharpOptions {
|
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):
|
* 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')
|
* 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning')
|
||||||
@ -1062,6 +1117,13 @@ declare namespace sharp {
|
|||||||
width?: number | undefined;
|
width?: number | undefined;
|
||||||
/** Number of pixels high (EXIF orientation is not taken into consideration) */
|
/** Number of pixels high (EXIF orientation is not taken into consideration) */
|
||||||
height?: number | undefined;
|
height?: number | undefined;
|
||||||
|
/** Any changed metadata after the image orientation is applied. */
|
||||||
|
autoOrient: {
|
||||||
|
/** Number of pixels wide (EXIF orientation is taken into consideration) */
|
||||||
|
width: number;
|
||||||
|
/** Number of pixels high (EXIF orientation is taken into consideration) */
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
/** Name of colour space interpretation */
|
/** Name of colour space interpretation */
|
||||||
space?: keyof ColourspaceEnum | undefined;
|
space?: keyof ColourspaceEnum | undefined;
|
||||||
/** Number of bands e.g. 3 for sRGB, 4 for CMYK */
|
/** Number of bands e.g. 3 for sRGB, 4 for CMYK */
|
||||||
@ -1512,6 +1574,8 @@ declare namespace sharp {
|
|||||||
failOn?: FailOnOptions | undefined;
|
failOn?: FailOnOptions | undefined;
|
||||||
/** see sharp() constructor, (optional, default 268402689) */
|
/** see sharp() constructor, (optional, default 268402689) */
|
||||||
limitInputPixels?: number | boolean | undefined;
|
limitInputPixels?: number | boolean | undefined;
|
||||||
|
/** see sharp() constructor, (optional, default false) */
|
||||||
|
autoOrient?: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TileOptions {
|
interface TileOptions {
|
||||||
|
27
lib/input.js
@ -24,9 +24,9 @@ const align = {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _inputOptionsFromObject (obj) {
|
function _inputOptionsFromObject (obj) {
|
||||||
const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground } = obj;
|
const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient } = obj;
|
||||||
return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground].some(is.defined)
|
return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient].some(is.defined)
|
||||||
? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground }
|
? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient }
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +36,7 @@ function _inputOptionsFromObject (obj) {
|
|||||||
*/
|
*/
|
||||||
function _createInputDescriptor (input, inputOptions, containerOptions) {
|
function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||||
const inputDescriptor = {
|
const inputDescriptor = {
|
||||||
|
autoOrient: false,
|
||||||
failOn: 'warning',
|
failOn: 'warning',
|
||||||
limitInputPixels: Math.pow(0x3FFF, 2),
|
limitInputPixels: Math.pow(0x3FFF, 2),
|
||||||
ignoreIcc: false,
|
ignoreIcc: false,
|
||||||
@ -93,6 +94,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
|
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// autoOrient
|
||||||
|
if (is.defined(inputOptions.autoOrient)) {
|
||||||
|
if (is.bool(inputOptions.autoOrient)) {
|
||||||
|
inputDescriptor.autoOrient = inputOptions.autoOrient;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('autoOrient', 'boolean', inputOptions.autoOrient);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Density
|
// Density
|
||||||
if (is.defined(inputOptions.density)) {
|
if (is.defined(inputOptions.density)) {
|
||||||
if (is.inRange(inputOptions.density, 1, 100000)) {
|
if (is.inRange(inputOptions.density, 1, 100000)) {
|
||||||
@ -475,15 +484,9 @@ function _isStreamInput () {
|
|||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Based on EXIF rotation metadata, get the right-side-up width and height:
|
* // Get dimensions taking EXIF Orientation into account.
|
||||||
*
|
* const { autoOrient } = await sharp(input).metadata();
|
||||||
* const size = getNormalSize(await sharp(input).metadata());
|
* const { width, height } = autoOrient;
|
||||||
*
|
|
||||||
* function getNormalSize({ width, height, orientation }) {
|
|
||||||
* return (orientation || 0) >= 5
|
|
||||||
* ? { width: height, height: width }
|
|
||||||
* : { width, height };
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* @param {Function} [callback] - called with the arguments `(err, metadata)`
|
* @param {Function} [callback] - called with the arguments `(err, metadata)`
|
||||||
* @returns {Promise<Object>|Sharp}
|
* @returns {Promise<Object>|Sharp}
|
||||||
|
@ -18,22 +18,19 @@ const vipsPrecision = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate the output image by either an explicit angle
|
* Rotate the output image.
|
||||||
* or auto-orient based on the EXIF `Orientation` tag.
|
|
||||||
*
|
*
|
||||||
* 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.
|
* For example, `-450` will produce a 270 degree rotation.
|
||||||
*
|
*
|
||||||
* When rotating by an angle other than a multiple of 90,
|
* When rotating by an angle other than a multiple of 90,
|
||||||
* the background colour can be provided with the `background` option.
|
* the background colour can be provided with the `background` option.
|
||||||
*
|
*
|
||||||
* If no angle is provided, it is determined from the EXIF data.
|
* For backwards compatibility, if no angle is provided, `.autoOrient()` will be called.
|
||||||
* 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.
|
* 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
|
||||||
* Only one rotation can occur per pipeline.
|
* pipeline will be ignored.
|
||||||
* Previous calls to `rotate` in the same pipeline will be ignored.
|
|
||||||
*
|
*
|
||||||
* Multi-page images can only be rotated by 180 degrees.
|
* Multi-page images can only be rotated by 180 degrees.
|
||||||
*
|
*
|
||||||
@ -41,17 +38,6 @@ const vipsPrecision = {
|
|||||||
* for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`.
|
* for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`.
|
||||||
*
|
*
|
||||||
* @example
|
* @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)
|
* const rotateThenResize = await sharp(input)
|
||||||
* .rotate(90)
|
* .rotate(90)
|
||||||
* .resize({ width: 16, height: 8, fit: 'fill' })
|
* .resize({ width: 16, height: 8, fit: 'fill' })
|
||||||
@ -68,12 +54,15 @@ const vipsPrecision = {
|
|||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
function rotate (angle, options) {
|
function rotate (angle, options) {
|
||||||
if (this.options.useExifOrientation || this.options.angle || this.options.rotationAngle) {
|
|
||||||
this.options.debuglog('ignoring previous rotate options');
|
|
||||||
}
|
|
||||||
if (!is.defined(angle)) {
|
if (!is.defined(angle)) {
|
||||||
this.options.useExifOrientation = true;
|
return this.autoOrient();
|
||||||
} else if (is.integer(angle) && !(angle % 90)) {
|
}
|
||||||
|
if (this.options.angle || this.options.rotationAngle) {
|
||||||
|
this.options.debuglog('ignoring previous rotate options');
|
||||||
|
this.options.angle = 0;
|
||||||
|
this.options.rotationAngle = 0;
|
||||||
|
}
|
||||||
|
if (is.integer(angle) && !(angle % 90)) {
|
||||||
this.options.angle = angle;
|
this.options.angle = angle;
|
||||||
} else if (is.number(angle)) {
|
} else if (is.number(angle)) {
|
||||||
this.options.rotationAngle = angle;
|
this.options.rotationAngle = angle;
|
||||||
@ -92,6 +81,34 @@ function rotate (angle, options) {
|
|||||||
return this;
|
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.
|
* Mirror the image vertically (up-down) about the x-axis.
|
||||||
* This always occurs before rotation, if any.
|
* This always occurs before rotation, if any.
|
||||||
@ -935,6 +952,7 @@ function modulate (options) {
|
|||||||
*/
|
*/
|
||||||
module.exports = function (Sharp) {
|
module.exports = function (Sharp) {
|
||||||
Object.assign(Sharp.prototype, {
|
Object.assign(Sharp.prototype, {
|
||||||
|
autoOrient,
|
||||||
rotate,
|
rotate,
|
||||||
flip,
|
flip,
|
||||||
flop,
|
flop,
|
||||||
|
@ -107,7 +107,7 @@ const mapFitToCanvas = {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function isRotationExpected (options) {
|
function isRotationExpected (options) {
|
||||||
return (options.angle % 360) !== 0 || options.useExifOrientation === true || options.rotationAngle !== 0;
|
return (options.angle % 360) !== 0 || options.input.autoOrient === true || options.rotationAngle !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,6 +166,8 @@ namespace sharp {
|
|||||||
descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
||||||
// Remove safety features and allow unlimited input
|
// Remove safety features and allow unlimited input
|
||||||
descriptor->unlimited = AttrAsBool(input, "unlimited");
|
descriptor->unlimited = AttrAsBool(input, "unlimited");
|
||||||
|
// Use the EXIF orientation to auto orient the image
|
||||||
|
descriptor->autoOrient = AttrAsBool(input, "autoOrient");
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ namespace sharp {
|
|||||||
struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
|
struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string file;
|
std::string file;
|
||||||
|
bool autoOrient;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
VipsFailOn failOn;
|
VipsFailOn failOn;
|
||||||
uint64_t limitInputPixels;
|
uint64_t limitInputPixels;
|
||||||
@ -73,6 +74,7 @@ namespace sharp {
|
|||||||
std::vector<double> pdfBackground;
|
std::vector<double> pdfBackground;
|
||||||
|
|
||||||
InputDescriptor():
|
InputDescriptor():
|
||||||
|
autoOrient(false),
|
||||||
buffer(nullptr),
|
buffer(nullptr),
|
||||||
failOn(VIPS_FAIL_ON_WARNING),
|
failOn(VIPS_FAIL_ON_WARNING),
|
||||||
limitInputPixels(0x3FFF * 0x3FFF),
|
limitInputPixels(0x3FFF * 0x3FFF),
|
||||||
|
@ -242,6 +242,15 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|||||||
if (baton->orientation > 0) {
|
if (baton->orientation > 0) {
|
||||||
info.Set("orientation", baton->orientation);
|
info.Set("orientation", baton->orientation);
|
||||||
}
|
}
|
||||||
|
Napi::Object autoOrient = Napi::Object::New(env);
|
||||||
|
info.Set("autoOrient", autoOrient);
|
||||||
|
if (baton->orientation >= 5) {
|
||||||
|
autoOrient.Set("width", baton->height);
|
||||||
|
autoOrient.Set("height", baton->width);
|
||||||
|
} else {
|
||||||
|
autoOrient.Set("width", baton->width);
|
||||||
|
autoOrient.Set("height", baton->height);
|
||||||
|
}
|
||||||
if (baton->exifLength > 0) {
|
if (baton->exifLength > 0) {
|
||||||
info.Set("exif", Napi::Buffer<char>::NewOrCopy(env, baton->exif, baton->exifLength, sharp::FreeCallback));
|
info.Set("exif", Napi::Buffer<char>::NewOrCopy(env, baton->exif, baton->exifLength, sharp::FreeCallback));
|
||||||
}
|
}
|
||||||
|
@ -63,14 +63,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
bool autoFlip = false;
|
bool autoFlip = false;
|
||||||
bool autoFlop = false;
|
bool autoFlop = false;
|
||||||
|
|
||||||
if (baton->useExifOrientation) {
|
if (baton->input->autoOrient) {
|
||||||
// Rotate and flip image according to Exif orientation
|
// Rotate and flip image according to Exif orientation
|
||||||
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
||||||
image = sharp::RemoveExifOrientation(image);
|
image = sharp::RemoveExifOrientation(image);
|
||||||
} else {
|
|
||||||
rotation = CalculateAngleRotation(baton->angle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rotation = CalculateAngleRotation(baton->angle);
|
||||||
|
|
||||||
// Rotate pre-extract
|
// Rotate pre-extract
|
||||||
bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
|
bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
|
||||||
(rotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
|
(rotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
|
||||||
@ -92,18 +92,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = image.rot(autoRotation);
|
image = image.rot(autoRotation);
|
||||||
autoRotation = VIPS_ANGLE_D0;
|
autoRotation = VIPS_ANGLE_D0;
|
||||||
}
|
}
|
||||||
if (autoFlip) {
|
if (autoFlip != baton->flip) {
|
||||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
||||||
autoFlip = false;
|
autoFlip = false;
|
||||||
} else if (baton->flip) {
|
|
||||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
||||||
baton->flip = false;
|
baton->flip = false;
|
||||||
}
|
}
|
||||||
if (autoFlop) {
|
if (autoFlop != baton->flop) {
|
||||||
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
||||||
autoFlop = false;
|
autoFlop = false;
|
||||||
} else if (baton->flop) {
|
|
||||||
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
||||||
baton->flop = false;
|
baton->flop = false;
|
||||||
}
|
}
|
||||||
if (rotation != VIPS_ANGLE_D0) {
|
if (rotation != VIPS_ANGLE_D0) {
|
||||||
@ -396,11 +392,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = image.rot(autoRotation);
|
image = image.rot(autoRotation);
|
||||||
}
|
}
|
||||||
// Mirror vertically (up-down) about the x-axis
|
// Mirror vertically (up-down) about the x-axis
|
||||||
if (baton->flip || autoFlip) {
|
if (baton->flip != autoFlip) {
|
||||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
||||||
}
|
}
|
||||||
// Mirror horizontally (left-right) about the y-axis
|
// Mirror horizontally (left-right) about the y-axis
|
||||||
if (baton->flop || autoFlop) {
|
if (baton->flop != autoFlop) {
|
||||||
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
||||||
}
|
}
|
||||||
// Rotate post-extract 90-angle
|
// Rotate post-extract 90-angle
|
||||||
@ -631,6 +627,30 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
composite->input->access = access;
|
composite->input->access = access;
|
||||||
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
||||||
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
|
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
|
||||||
|
|
||||||
|
if (composite->input->autoOrient) {
|
||||||
|
// Respect EXIF Orientation
|
||||||
|
VipsAngle compositeAutoRotation = VIPS_ANGLE_D0;
|
||||||
|
bool compositeAutoFlip = false;
|
||||||
|
bool compositeAutoFlop = false;
|
||||||
|
std::tie(compositeAutoRotation, compositeAutoFlip, compositeAutoFlop) =
|
||||||
|
CalculateExifRotationAndFlip(sharp::ExifOrientation(compositeImage));
|
||||||
|
|
||||||
|
compositeImage = sharp::RemoveExifOrientation(compositeImage);
|
||||||
|
compositeImage = sharp::StaySequential(compositeImage,
|
||||||
|
compositeAutoRotation != VIPS_ANGLE_D0 || compositeAutoFlip);
|
||||||
|
|
||||||
|
if (compositeAutoRotation != VIPS_ANGLE_D0) {
|
||||||
|
compositeImage = compositeImage.rot(compositeAutoRotation);
|
||||||
|
}
|
||||||
|
if (compositeAutoFlip) {
|
||||||
|
compositeImage = compositeImage.flip(VIPS_DIRECTION_VERTICAL);
|
||||||
|
}
|
||||||
|
if (compositeAutoFlop) {
|
||||||
|
compositeImage = compositeImage.flip(VIPS_DIRECTION_HORIZONTAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify within current dimensions
|
// Verify within current dimensions
|
||||||
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
||||||
throw vips::VError("Image to composite must have same dimensions or smaller");
|
throw vips::VError("Image to composite must have same dimensions or smaller");
|
||||||
@ -1567,7 +1587,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
||||||
baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
|
baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
|
||||||
baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
|
baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
|
||||||
baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
|
|
||||||
baton->angle = sharp::AttrAsInt32(options, "angle");
|
baton->angle = sharp::AttrAsInt32(options, "angle");
|
||||||
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
||||||
baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
|
baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
|
||||||
|
@ -109,7 +109,6 @@ struct PipelineBaton {
|
|||||||
int claheWidth;
|
int claheWidth;
|
||||||
int claheHeight;
|
int claheHeight;
|
||||||
int claheMaxSlope;
|
int claheMaxSlope;
|
||||||
bool useExifOrientation;
|
|
||||||
int angle;
|
int angle;
|
||||||
double rotationAngle;
|
double rotationAngle;
|
||||||
std::vector<double> rotationBackground;
|
std::vector<double> rotationBackground;
|
||||||
@ -282,7 +281,6 @@ struct PipelineBaton {
|
|||||||
claheWidth(0),
|
claheWidth(0),
|
||||||
claheHeight(0),
|
claheHeight(0),
|
||||||
claheMaxSlope(3),
|
claheMaxSlope(3),
|
||||||
useExifOrientation(false),
|
|
||||||
angle(0),
|
angle(0),
|
||||||
rotationAngle(0.0),
|
rotationAngle(0.0),
|
||||||
rotationBackground{ 0.0, 0.0, 0.0, 255.0 },
|
rotationBackground{ 0.0, 0.0, 0.0, 255.0 },
|
||||||
|
BIN
test/fixtures/expected/Landscape_1_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
test/fixtures/expected/Landscape_1_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_1_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
test/fixtures/expected/Landscape_1_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_1_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
test/fixtures/expected/Landscape_1_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Landscape_1_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_2_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_2_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_2_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_2_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_2_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_2_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
test/fixtures/expected/Landscape_2_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_3_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_3_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_3_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_3_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_3_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_3_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_3_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_4_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_4_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_4_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_4_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_4_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_4_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
test/fixtures/expected/Landscape_4_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_5_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_5_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_5_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_5_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_5_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_5_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_5_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_6_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_6_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_6_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_6_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_6_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_6_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/fixtures/expected/Landscape_6_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_7_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_7_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_7_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_7_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_7_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_7_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
test/fixtures/expected/Landscape_7_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
test/fixtures/expected/Landscape_8_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_8_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_8_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_8_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Landscape_8_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
test/fixtures/expected/Landscape_8_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
test/fixtures/expected/Landscape_8_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
test/fixtures/expected/Portrait_1_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
test/fixtures/expected/Portrait_1_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_1_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_1_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_1_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
test/fixtures/expected/Portrait_1_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
test/fixtures/expected/Portrait_1_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
test/fixtures/expected/Portrait_2_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
test/fixtures/expected/Portrait_2_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_2_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_2_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_2_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_2_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_2_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
test/fixtures/expected/Portrait_3_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
test/fixtures/expected/Portrait_3_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_3_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_3_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_3_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test/fixtures/expected/Portrait_3_rotate45-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_3_rotate90-out.jpg
vendored
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
test/fixtures/expected/Portrait_4_flip-out.jpg
vendored
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
test/fixtures/expected/Portrait_4_flip_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_4_flop-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_4_rotate180-out.jpg
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
test/fixtures/expected/Portrait_4_rotate270-out.jpg
vendored
Normal file
After Width: | Height: | Size: 72 KiB |