From ab0e0513c259c23198bdad124413b43514819bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Mon, 30 Oct 2023 16:02:29 +0100 Subject: [PATCH] [expo-image-manipulator] add extent action --- packages/expo-image-manipulator/CHANGELOG.md | 2 + .../build/ExpoImageManipulator.web.d.ts.map | 2 +- .../build/ExpoImageManipulator.web.js | 5 ++- .../build/ExpoImageManipulator.web.js.map | 2 +- .../build/ImageManipulator.types.d.ts | 18 +++++++- .../build/ImageManipulator.types.d.ts.map | 2 +- .../build/ImageManipulator.types.js.map | 2 +- .../build/actions/ExtentAction.web.d.ts | 4 ++ .../build/actions/ExtentAction.web.d.ts.map | 1 + .../build/actions/ExtentAction.web.js | 28 ++++++++++++ .../build/actions/ExtentAction.web.js.map | 1 + .../build/actions/index.web.d.ts | 1 + .../build/actions/index.web.d.ts.map | 2 +- .../build/actions/index.web.js | 1 + .../build/actions/index.web.js.map | 2 +- .../build/validators.d.ts.map | 2 +- .../build/validators.js | 17 +++++++- .../build/validators.js.map | 2 +- .../src/ExpoImageManipulator.web.ts | 4 +- .../src/ImageManipulator.types.ts | 20 ++++++++- .../src/__tests__/validators-test.ts | 31 +++++++++++++ .../src/actions/ExtentAction.web.ts | 43 +++++++++++++++++++ .../src/actions/index.web.ts | 1 + .../expo-image-manipulator/src/validators.ts | 21 ++++++++- 24 files changed, 200 insertions(+), 14 deletions(-) create mode 100644 packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts create mode 100644 packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts.map create mode 100644 packages/expo-image-manipulator/build/actions/ExtentAction.web.js create mode 100644 packages/expo-image-manipulator/build/actions/ExtentAction.web.js.map create mode 100644 packages/expo-image-manipulator/src/actions/ExtentAction.web.ts diff --git a/packages/expo-image-manipulator/CHANGELOG.md b/packages/expo-image-manipulator/CHANGELOG.md index da82a68ff3c13..f07d57e4ce585 100644 --- a/packages/expo-image-manipulator/CHANGELOG.md +++ b/packages/expo-image-manipulator/CHANGELOG.md @@ -8,6 +8,8 @@ ### 🎉 New features +- Added `extent` action ([#25116](https://github.com/expo/expo/pull/25116) by [@LinusU](https://github.com/LinusU)) + ### 🐛 Bug fixes ### 💡 Others diff --git a/packages/expo-image-manipulator/build/ExpoImageManipulator.web.d.ts.map b/packages/expo-image-manipulator/build/ExpoImageManipulator.web.d.ts.map index f2dd3a504464d..d9390282d2c88 100644 --- a/packages/expo-image-manipulator/build/ExpoImageManipulator.web.d.ts.map +++ b/packages/expo-image-manipulator/build/ExpoImageManipulator.web.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ExpoImageManipulator.web.d.ts","sourceRoot":"","sources":["../src/ExpoImageManipulator.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;;;yBAiDnE,MAAM,0CAEF,WAAW,GACnB,QAAQ,WAAW,CAAC;;AARzB,wBA2BE"} \ No newline at end of file +{"version":3,"file":"ExpoImageManipulator.web.d.ts","sourceRoot":"","sources":["../src/ExpoImageManipulator.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;;;yBAiDnE,MAAM,0CAEF,WAAW,GACnB,QAAQ,WAAW,CAAC;;AARzB,wBA6BE"} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js b/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js index 0a11284fb2bde..5b77ad6eb24fd 100644 --- a/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js +++ b/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js @@ -1,4 +1,4 @@ -import { crop, flip, resize, rotate } from './actions/index.web'; +import { crop, extent, flip, resize, rotate } from './actions/index.web'; import { getContext } from './utils/getContext.web'; function getResults(canvas, options) { let uri; @@ -47,6 +47,9 @@ export default { if ('crop' in action) { return crop(canvas, action.crop); } + else if ('extent' in action) { + return extent(canvas, action.extent); + } else if ('resize' in action) { return resize(canvas, action.resize); } diff --git a/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js.map b/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js.map index c4b03fae392df..83d972e93544d 100644 --- a/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js.map +++ b/packages/expo-image-manipulator/build/ExpoImageManipulator.web.js.map @@ -1 +1 @@ -{"version":3,"file":"ExpoImageManipulator.web.js","sourceRoot":"","sources":["../src/ExpoImageManipulator.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { ImageResult, SaveOptions, Action } from './ImageManipulator.types';\nimport { crop, flip, resize, rotate } from './actions/index.web';\nimport { getContext } from './utils/getContext.web';\n\nfunction getResults(canvas: HTMLCanvasElement, options?: SaveOptions): ImageResult {\n let uri: string;\n if (options) {\n const { format = 'png' } = options;\n if (options.format === 'png' && options.compress !== undefined) {\n console.warn('compress is not supported with png format.');\n }\n const quality = Math.min(1, Math.max(0, options.compress ?? 1));\n uri = canvas.toDataURL('image/' + format, quality);\n } else {\n // defaults to PNG with no loss\n uri = canvas.toDataURL();\n }\n return {\n uri,\n width: canvas.width,\n height: canvas.height,\n base64: uri.replace(/^data:image\\/\\w+;base64,/, ''),\n };\n}\n\nfunction loadImageAsync(uri: string): Promise {\n return new Promise((resolve, reject) => {\n const imageSource = new Image();\n imageSource.crossOrigin = 'anonymous';\n const canvas = document.createElement('canvas');\n imageSource.onload = () => {\n canvas.width = imageSource.naturalWidth;\n canvas.height = imageSource.naturalHeight;\n\n const context = getContext(canvas);\n context.drawImage(imageSource, 0, 0, imageSource.naturalWidth, imageSource.naturalHeight);\n\n resolve(canvas);\n };\n imageSource.onerror = () => reject(canvas);\n imageSource.src = uri;\n });\n}\n\nexport default {\n get name(): string {\n return 'ExpoImageManipulator';\n },\n async manipulateAsync(\n uri: string,\n actions: Action[] = [],\n options: SaveOptions\n ): Promise {\n const originalCanvas = await loadImageAsync(uri);\n\n const resultCanvas = actions.reduce((canvas, action) => {\n if ('crop' in action) {\n return crop(canvas, action.crop);\n } else if ('resize' in action) {\n return resize(canvas, action.resize);\n } else if ('flip' in action) {\n return flip(canvas, action.flip);\n } else if ('rotate' in action) {\n return rotate(canvas, action.rotate);\n } else {\n return canvas;\n }\n }, originalCanvas);\n\n return getResults(resultCanvas, options);\n },\n};\n"]} \ No newline at end of file +{"version":3,"file":"ExpoImageManipulator.web.js","sourceRoot":"","sources":["../src/ExpoImageManipulator.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { ImageResult, SaveOptions, Action } from './ImageManipulator.types';\nimport { crop, extent, flip, resize, rotate } from './actions/index.web';\nimport { getContext } from './utils/getContext.web';\n\nfunction getResults(canvas: HTMLCanvasElement, options?: SaveOptions): ImageResult {\n let uri: string;\n if (options) {\n const { format = 'png' } = options;\n if (options.format === 'png' && options.compress !== undefined) {\n console.warn('compress is not supported with png format.');\n }\n const quality = Math.min(1, Math.max(0, options.compress ?? 1));\n uri = canvas.toDataURL('image/' + format, quality);\n } else {\n // defaults to PNG with no loss\n uri = canvas.toDataURL();\n }\n return {\n uri,\n width: canvas.width,\n height: canvas.height,\n base64: uri.replace(/^data:image\\/\\w+;base64,/, ''),\n };\n}\n\nfunction loadImageAsync(uri: string): Promise {\n return new Promise((resolve, reject) => {\n const imageSource = new Image();\n imageSource.crossOrigin = 'anonymous';\n const canvas = document.createElement('canvas');\n imageSource.onload = () => {\n canvas.width = imageSource.naturalWidth;\n canvas.height = imageSource.naturalHeight;\n\n const context = getContext(canvas);\n context.drawImage(imageSource, 0, 0, imageSource.naturalWidth, imageSource.naturalHeight);\n\n resolve(canvas);\n };\n imageSource.onerror = () => reject(canvas);\n imageSource.src = uri;\n });\n}\n\nexport default {\n get name(): string {\n return 'ExpoImageManipulator';\n },\n async manipulateAsync(\n uri: string,\n actions: Action[] = [],\n options: SaveOptions\n ): Promise {\n const originalCanvas = await loadImageAsync(uri);\n\n const resultCanvas = actions.reduce((canvas, action) => {\n if ('crop' in action) {\n return crop(canvas, action.crop);\n } else if ('extent' in action) {\n return extent(canvas, action.extent);\n } else if ('resize' in action) {\n return resize(canvas, action.resize);\n } else if ('flip' in action) {\n return flip(canvas, action.flip);\n } else if ('rotate' in action) {\n return rotate(canvas, action.rotate);\n } else {\n return canvas;\n }\n }, originalCanvas);\n\n return getResults(resultCanvas, options);\n },\n};\n"]} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts b/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts index c53f38cc07a01..d10da833372cb 100644 --- a/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts +++ b/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts @@ -58,7 +58,23 @@ export type ActionCrop = { height: number; }; }; -export type Action = ActionResize | ActionRotate | ActionFlip | ActionCrop; +export type ActionExtent = { + /** + * **Set the image size and offset** + * + * If the image is enlarged, unfilled areas are set to the `backgroundColor`. To position the image, use `originX` and `originY`. + * + * @platform web + */ + extent: { + backgroundColor?: string | null; + originX?: number; + originY?: number; + width: number; + height: number; + }; +}; +export type Action = ActionResize | ActionRotate | ActionFlip | ActionCrop | ActionExtent; export declare enum SaveFormat { JPEG = "jpeg", PNG = "png", diff --git a/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts.map b/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts.map index 08c34037d6067..d1c414680351d 100644 --- a/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts.map +++ b/packages/expo-image-manipulator/build/ImageManipulator.types.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ImageManipulator.types.d.ts","sourceRoot":"","sources":["../src/ImageManipulator.types.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,MAAM,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,oBAAY,QAAQ;IAClB,QAAQ,aAAa;IACrB,UAAU,eAAe;CAC1B;AAGD,MAAM,MAAM,UAAU,GAAG;IACvB;;;OAGG;IACH,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AAGF,MAAM,MAAM,UAAU,GAAG;IACvB;;OAEG;IACH,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,CAAC;AAG3E,oBAAY,UAAU;IACpB,IAAI,SAAS;IACb,GAAG,QAAQ;IACX;;OAEG;IACH,IAAI,SAAS;CACd;AAGD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAAC"} \ No newline at end of file +{"version":3,"file":"ImageManipulator.types.d.ts","sourceRoot":"","sources":["../src/ImageManipulator.types.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,MAAM,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,oBAAY,QAAQ;IAClB,QAAQ,aAAa;IACrB,UAAU,eAAe;CAC1B;AAGD,MAAM,MAAM,UAAU,GAAG;IACvB;;;OAGG;IACH,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AAGF,MAAM,MAAM,UAAU,GAAG;IACvB;;OAEG;IACH,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;OAMG;IACH,MAAM,EAAE;QACN,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;AAG1F,oBAAY,UAAU;IACpB,IAAI,SAAS;IACb,GAAG,QAAQ;IACX;;OAEG;IACH,IAAI,SAAS;CACd;AAGD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAAC"} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/ImageManipulator.types.js.map b/packages/expo-image-manipulator/build/ImageManipulator.types.js.map index 4f43310b0c331..21d3417aa1547 100644 --- a/packages/expo-image-manipulator/build/ImageManipulator.types.js.map +++ b/packages/expo-image-manipulator/build/ImageManipulator.types.js.map @@ -1 +1 @@ -{"version":3,"file":"ImageManipulator.types.js","sourceRoot":"","sources":["../src/ImageManipulator.types.ts"],"names":[],"mappings":"AA4CA,eAAe;AACf,MAAM,CAAN,IAAY,QAGX;AAHD,WAAY,QAAQ;IAClB,iCAAqB,CAAA;IACrB,qCAAyB,CAAA;AAC3B,CAAC,EAHW,QAAQ,KAAR,QAAQ,QAGnB;AA2BD,eAAe;AACf,MAAM,CAAN,IAAY,UAOX;AAPD,WAAY,UAAU;IACpB,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX;;OAEG;IACH,2BAAa,CAAA;AACf,CAAC,EAPW,UAAU,KAAV,UAAU,QAOrB","sourcesContent":["// @needsAudit\nexport type ImageResult = {\n /**\n * An URI to the modified image (usable as the source for an `Image` or `Video` element).\n */\n uri: string;\n /**\n * Width of the image or video.\n */\n width: number;\n /**\n * Height of the image or video.\n */\n height: number;\n /**\n * It is included if the `base64` save option was truthy, and is a string containing the\n * JPEG/PNG (depending on `format`) data of the image in Base64. Prepend that with `'data:image/xxx;base64,'`\n * to get a data URI, which you can use as the source for an `Image` element for example\n * (where `xxx` is `jpeg` or `png`).\n */\n base64?: string;\n};\n\n// @needsAudit\nexport type ActionResize = {\n /**\n * Values correspond to the result image dimensions. If you specify only one value, the other will\n * be calculated automatically to preserve image ratio.\n */\n resize: {\n width?: number;\n height?: number;\n };\n};\n\n// @needsAudit\nexport type ActionRotate = {\n /**\n * Degrees to rotate the image. Rotation is clockwise when the value is positive and\n * counter-clockwise when negative.\n */\n rotate: number;\n};\n\n// @docsMissing\nexport enum FlipType {\n Vertical = 'vertical',\n Horizontal = 'horizontal',\n}\n\n// @needsAudit\nexport type ActionFlip = {\n /**\n * An axis on which image will be flipped. Only one flip per transformation is available. If you\n * want to flip according to both axes then provide two separate transformations.\n */\n flip: FlipType;\n};\n\n// @needsAudit\nexport type ActionCrop = {\n /**\n * Fields specify top-left corner and dimensions of a crop rectangle.\n */\n crop: {\n originX: number;\n originY: number;\n width: number;\n height: number;\n };\n};\n\n// @docsMissing\nexport type Action = ActionResize | ActionRotate | ActionFlip | ActionCrop;\n\n// @docsMissing\nexport enum SaveFormat {\n JPEG = 'jpeg',\n PNG = 'png',\n /**\n * @platform web\n */\n WEBP = 'webp',\n}\n\n// @needsAudit\n/**\n * A map defining how modified image should be saved.\n */\nexport type SaveOptions = {\n /**\n * Whether to also include the image data in Base64 format.\n */\n base64?: boolean;\n /**\n * A value in range `0.0` - `1.0` specifying compression level of the result image. `1` means\n * no compression (highest quality) and `0` the highest compression (lowest quality).\n */\n compress?: number;\n /**\n * Specifies what type of compression should be used and what is the result file extension.\n * `SaveFormat.PNG` compression is lossless but slower, `SaveFormat.JPEG` is faster but the image\n * has visible artifacts. Defaults to `SaveFormat.JPEG`\n */\n format?: SaveFormat;\n};\n"]} \ No newline at end of file +{"version":3,"file":"ImageManipulator.types.js","sourceRoot":"","sources":["../src/ImageManipulator.types.ts"],"names":[],"mappings":"AA4CA,eAAe;AACf,MAAM,CAAN,IAAY,QAGX;AAHD,WAAY,QAAQ;IAClB,iCAAqB,CAAA;IACrB,qCAAyB,CAAA;AAC3B,CAAC,EAHW,QAAQ,KAAR,QAAQ,QAGnB;AA6CD,eAAe;AACf,MAAM,CAAN,IAAY,UAOX;AAPD,WAAY,UAAU;IACpB,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX;;OAEG;IACH,2BAAa,CAAA;AACf,CAAC,EAPW,UAAU,KAAV,UAAU,QAOrB","sourcesContent":["// @needsAudit\nexport type ImageResult = {\n /**\n * An URI to the modified image (usable as the source for an `Image` or `Video` element).\n */\n uri: string;\n /**\n * Width of the image or video.\n */\n width: number;\n /**\n * Height of the image or video.\n */\n height: number;\n /**\n * It is included if the `base64` save option was truthy, and is a string containing the\n * JPEG/PNG (depending on `format`) data of the image in Base64. Prepend that with `'data:image/xxx;base64,'`\n * to get a data URI, which you can use as the source for an `Image` element for example\n * (where `xxx` is `jpeg` or `png`).\n */\n base64?: string;\n};\n\n// @needsAudit\nexport type ActionResize = {\n /**\n * Values correspond to the result image dimensions. If you specify only one value, the other will\n * be calculated automatically to preserve image ratio.\n */\n resize: {\n width?: number;\n height?: number;\n };\n};\n\n// @needsAudit\nexport type ActionRotate = {\n /**\n * Degrees to rotate the image. Rotation is clockwise when the value is positive and\n * counter-clockwise when negative.\n */\n rotate: number;\n};\n\n// @docsMissing\nexport enum FlipType {\n Vertical = 'vertical',\n Horizontal = 'horizontal',\n}\n\n// @needsAudit\nexport type ActionFlip = {\n /**\n * An axis on which image will be flipped. Only one flip per transformation is available. If you\n * want to flip according to both axes then provide two separate transformations.\n */\n flip: FlipType;\n};\n\n// @needsAudit\nexport type ActionCrop = {\n /**\n * Fields specify top-left corner and dimensions of a crop rectangle.\n */\n crop: {\n originX: number;\n originY: number;\n width: number;\n height: number;\n };\n};\n\n// @needsAudit\nexport type ActionExtent = {\n /**\n * **Set the image size and offset**\n *\n * If the image is enlarged, unfilled areas are set to the `backgroundColor`. To position the image, use `originX` and `originY`.\n *\n * @platform web\n */\n extent: {\n backgroundColor?: string | null;\n originX?: number;\n originY?: number;\n width: number;\n height: number;\n };\n};\n\n// @docsMissing\nexport type Action = ActionResize | ActionRotate | ActionFlip | ActionCrop | ActionExtent;\n\n// @docsMissing\nexport enum SaveFormat {\n JPEG = 'jpeg',\n PNG = 'png',\n /**\n * @platform web\n */\n WEBP = 'webp',\n}\n\n// @needsAudit\n/**\n * A map defining how modified image should be saved.\n */\nexport type SaveOptions = {\n /**\n * Whether to also include the image data in Base64 format.\n */\n base64?: boolean;\n /**\n * A value in range `0.0` - `1.0` specifying compression level of the result image. `1` means\n * no compression (highest quality) and `0` the highest compression (lowest quality).\n */\n compress?: number;\n /**\n * Specifies what type of compression should be used and what is the result file extension.\n * `SaveFormat.PNG` compression is lossless but slower, `SaveFormat.JPEG` is faster but the image\n * has visible artifacts. Defaults to `SaveFormat.JPEG`\n */\n format?: SaveFormat;\n};\n"]} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts b/packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts new file mode 100644 index 0000000000000..89cb6f7069f68 --- /dev/null +++ b/packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts @@ -0,0 +1,4 @@ +import { ActionExtent } from '../ImageManipulator.types'; +declare const _default: (canvas: HTMLCanvasElement, options: ActionExtent['extent']) => HTMLCanvasElement; +export default _default; +//# sourceMappingURL=ExtentAction.web.d.ts.map \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts.map b/packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts.map new file mode 100644 index 0000000000000..7819570b4e402 --- /dev/null +++ b/packages/expo-image-manipulator/build/actions/ExtentAction.web.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ExtentAction.web.d.ts","sourceRoot":"","sources":["../../src/actions/ExtentAction.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;iCAGjC,iBAAiB,WAAW,YAAY,CAAC,QAAQ,CAAC;AAA1E,wBAqCE"} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/actions/ExtentAction.web.js b/packages/expo-image-manipulator/build/actions/ExtentAction.web.js new file mode 100644 index 0000000000000..830e2cd963a72 --- /dev/null +++ b/packages/expo-image-manipulator/build/actions/ExtentAction.web.js @@ -0,0 +1,28 @@ +import { CodedError } from 'expo-modules-core'; +import { getContext } from '../utils/getContext.web'; +export default (canvas, options) => { + // ensure values are defined. + const { backgroundColor = null, originX = 0, originY = 0, width = 0, height = 0 } = options; + if (width === 0 || height === 0) { + throw new CodedError('ERR_IMAGE_MANIPULATOR_EXTENT', 'Extent size must be greater than 0: ' + JSON.stringify(options, null, 2)); + } + const result = document.createElement('canvas'); + result.width = width; + result.height = height; + const sx = originX < 0 ? 0 : originX; + const sy = originY < 0 ? 0 : originY; + const sw = originX < 0 ? Math.min(canvas.width, width + originX) : Math.min(canvas.width - originX, width); + const sh = originY < 0 + ? Math.min(canvas.height, height + originY) + : Math.min(canvas.height - originY, height); + const dx = originX < 0 ? -originX : 0; + const dy = originY < 0 ? -originY : 0; + const context = getContext(result); + if (backgroundColor != null) { + context.fillStyle = backgroundColor; + context.fillRect(0, 0, width, height); + } + context.drawImage(canvas, sx, sy, sw, sh, dx, dy, sw, sh); + return result; +}; +//# sourceMappingURL=ExtentAction.web.js.map \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/actions/ExtentAction.web.js.map b/packages/expo-image-manipulator/build/actions/ExtentAction.web.js.map new file mode 100644 index 0000000000000..403d135db8585 --- /dev/null +++ b/packages/expo-image-manipulator/build/actions/ExtentAction.web.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ExtentAction.web.js","sourceRoot":"","sources":["../../src/actions/ExtentAction.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,eAAe,CAAC,MAAyB,EAAE,OAA+B,EAAE,EAAE;IAC5E,6BAA6B;IAC7B,MAAM,EAAE,eAAe,GAAG,IAAI,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAE5F,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,EAAE;QAC/B,MAAM,IAAI,UAAU,CAClB,8BAA8B,EAC9B,sCAAsC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAC1E,CAAC;KACH;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IAEvB,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACrC,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACrC,MAAM,EAAE,GACN,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC;IAClG,MAAM,EAAE,GACN,OAAO,GAAG,CAAC;QACT,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAC3C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;IAEhD,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtC,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAEnC,IAAI,eAAe,IAAI,IAAI,EAAE;QAC3B,OAAO,CAAC,SAAS,GAAG,eAAe,CAAC;QACpC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;KACvC;IAED,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAE1D,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC","sourcesContent":["import { CodedError } from 'expo-modules-core';\n\nimport { ActionExtent } from '../ImageManipulator.types';\nimport { getContext } from '../utils/getContext.web';\n\nexport default (canvas: HTMLCanvasElement, options: ActionExtent['extent']) => {\n // ensure values are defined.\n const { backgroundColor = null, originX = 0, originY = 0, width = 0, height = 0 } = options;\n\n if (width === 0 || height === 0) {\n throw new CodedError(\n 'ERR_IMAGE_MANIPULATOR_EXTENT',\n 'Extent size must be greater than 0: ' + JSON.stringify(options, null, 2)\n );\n }\n\n const result = document.createElement('canvas');\n result.width = width;\n result.height = height;\n\n const sx = originX < 0 ? 0 : originX;\n const sy = originY < 0 ? 0 : originY;\n const sw =\n originX < 0 ? Math.min(canvas.width, width + originX) : Math.min(canvas.width - originX, width);\n const sh =\n originY < 0\n ? Math.min(canvas.height, height + originY)\n : Math.min(canvas.height - originY, height);\n\n const dx = originX < 0 ? -originX : 0;\n const dy = originY < 0 ? -originY : 0;\n\n const context = getContext(result);\n\n if (backgroundColor != null) {\n context.fillStyle = backgroundColor;\n context.fillRect(0, 0, width, height);\n }\n\n context.drawImage(canvas, sx, sy, sw, sh, dx, dy, sw, sh);\n\n return result;\n};\n"]} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/actions/index.web.d.ts b/packages/expo-image-manipulator/build/actions/index.web.d.ts index d582cca16198a..5734251747591 100644 --- a/packages/expo-image-manipulator/build/actions/index.web.d.ts +++ b/packages/expo-image-manipulator/build/actions/index.web.d.ts @@ -1,4 +1,5 @@ export { default as crop } from './CropAction.web'; +export { default as extent } from './ExtentAction.web'; export { default as flip } from './FlipAction.web'; export { default as resize } from './ResizeAction.web'; export { default as rotate } from './RotateAction.web'; diff --git a/packages/expo-image-manipulator/build/actions/index.web.d.ts.map b/packages/expo-image-manipulator/build/actions/index.web.d.ts.map index 027789d4a6fb6..51d1f4237b6c2 100644 --- a/packages/expo-image-manipulator/build/actions/index.web.d.ts.map +++ b/packages/expo-image-manipulator/build/actions/index.web.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../src/actions/index.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC"} \ No newline at end of file +{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../src/actions/index.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC"} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/actions/index.web.js b/packages/expo-image-manipulator/build/actions/index.web.js index 5dfad09126f66..6db13c6e1584b 100644 --- a/packages/expo-image-manipulator/build/actions/index.web.js +++ b/packages/expo-image-manipulator/build/actions/index.web.js @@ -1,4 +1,5 @@ export { default as crop } from './CropAction.web'; +export { default as extent } from './ExtentAction.web'; export { default as flip } from './FlipAction.web'; export { default as resize } from './ResizeAction.web'; export { default as rotate } from './RotateAction.web'; diff --git a/packages/expo-image-manipulator/build/actions/index.web.js.map b/packages/expo-image-manipulator/build/actions/index.web.js.map index fe3d687d7842c..7bdbdc44a9760 100644 --- a/packages/expo-image-manipulator/build/actions/index.web.js.map +++ b/packages/expo-image-manipulator/build/actions/index.web.js.map @@ -1 +1 @@ -{"version":3,"file":"index.web.js","sourceRoot":"","sources":["../../src/actions/index.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export { default as crop } from './CropAction.web';\nexport { default as flip } from './FlipAction.web';\nexport { default as resize } from './ResizeAction.web';\nexport { default as rotate } from './RotateAction.web';\n"]} \ No newline at end of file +{"version":3,"file":"index.web.js","sourceRoot":"","sources":["../../src/actions/index.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export { default as crop } from './CropAction.web';\nexport { default as extent } from './ExtentAction.web';\nexport { default as flip } from './FlipAction.web';\nexport { default as resize } from './ResizeAction.web';\nexport { default as rotate } from './RotateAction.web';\n"]} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/validators.d.ts.map b/packages/expo-image-manipulator/build/validators.d.ts.map index de9ac3ea7d8b9..9f39666250855 100644 --- a/packages/expo-image-manipulator/build/validators.d.ts.map +++ b/packages/expo-image-manipulator/build/validators.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAON,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,QAIzF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI7C;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CA8BvD;AA6CD,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,IAAI,CAgBnF"} \ No newline at end of file +{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAQN,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,QAIzF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI7C;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAgCvD;AA6DD,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,IAAI,CAgBnF"} \ No newline at end of file diff --git a/packages/expo-image-manipulator/build/validators.js b/packages/expo-image-manipulator/build/validators.js index 2f214b8476db7..cb8d0d676fe93 100644 --- a/packages/expo-image-manipulator/build/validators.js +++ b/packages/expo-image-manipulator/build/validators.js @@ -17,7 +17,7 @@ export function validateActions(actions) { if (typeof action !== 'object' || action === null) { throw new TypeError('Action must be an object'); } - const supportedActionTypes = ['crop', 'flip', 'rotate', 'resize']; + const supportedActionTypes = ['crop', 'extent', 'flip', 'rotate', 'resize']; const actionKeys = Object.keys(action); if (actionKeys.length !== 1) { throw new TypeError(`Single action must contain exactly one transformation: ${supportedActionTypes.join(', ')}`); @@ -29,6 +29,9 @@ export function validateActions(actions) { if (actionType === 'crop') { validateCropAction(action); } + else if (actionType === 'extent') { + validateExtentAction(action); + } else if (actionType === 'flip') { validateFlipAction(action); } @@ -51,6 +54,18 @@ function validateCropAction(action) { throw new TypeError('Crop action must be an object of shape { originX: number; originY: number; width: number; height: number }'); } } +function validateExtentAction(action) { + const isValid = typeof action.extent === 'object' && + action.extent !== null && + (action.extent.backgroundColor == null || typeof action.extent.backgroundColor === 'string') && + (action.extent.originX == null || typeof action.extent.originX === 'number') && + (action.extent.originY == null || typeof action.extent.originY === 'number') && + typeof action.extent.width === 'number' && + typeof action.extent.height === 'number'; + if (!isValid) { + throw new TypeError('Extent action must be an object of shape { backgroundColor?: string; originX?: number; originY?: number; width: number; height: number }'); + } +} function validateFlipAction(action) { if (typeof action.flip !== 'string' || ![FlipType.Horizontal, FlipType.Vertical].includes(action.flip)) { diff --git a/packages/expo-image-manipulator/build/validators.js.map b/packages/expo-image-manipulator/build/validators.js.map index 84d0a3a9bee9c..d24d63605ca2a 100644 --- a/packages/expo-image-manipulator/build/validators.js.map +++ b/packages/expo-image-manipulator/build/validators.js.map @@ -1 +1 @@ -{"version":3,"file":"validators.js","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Action,\n ActionCrop,\n ActionFlip,\n ActionResize,\n ActionRotate,\n FlipType,\n SaveFormat,\n SaveOptions,\n} from './ImageManipulator.types';\n\nexport function validateArguments(uri: string, actions: Action[], saveOptions: SaveOptions) {\n validateUri(uri);\n validateActions(actions);\n validateSaveOptions(saveOptions);\n}\n\nexport function validateUri(uri: string): void {\n if (!(typeof uri === 'string')) {\n throw new TypeError('The \"uri\" argument must be a string');\n }\n}\n\nexport function validateActions(actions: Action[]): void {\n if (!Array.isArray(actions)) {\n throw new TypeError('The \"actions\" argument must be an array');\n }\n for (const action of actions) {\n if (typeof action !== 'object' || action === null) {\n throw new TypeError('Action must be an object');\n }\n const supportedActionTypes = ['crop', 'flip', 'rotate', 'resize'];\n const actionKeys = Object.keys(action);\n if (actionKeys.length !== 1) {\n throw new TypeError(\n `Single action must contain exactly one transformation: ${supportedActionTypes.join(', ')}`\n );\n }\n const actionType = actionKeys[0];\n if (!supportedActionTypes.includes(actionType)) {\n throw new TypeError(`Unsupported action type: ${actionType}`);\n }\n\n if (actionType === 'crop') {\n validateCropAction(action as ActionCrop);\n } else if (actionType === 'flip') {\n validateFlipAction(action as ActionFlip);\n } else if (actionType === 'rotate') {\n validateRotateAction(action as ActionRotate);\n } else if (actionType === 'resize') {\n validateResizeAction(action as ActionResize);\n }\n }\n}\n\nfunction validateCropAction(action: ActionCrop): void {\n const isValid =\n typeof action.crop === 'object' &&\n action.crop !== null &&\n typeof action.crop.originX === 'number' &&\n typeof action.crop.originY === 'number' &&\n typeof action.crop.width === 'number' &&\n typeof action.crop.height === 'number';\n if (!isValid) {\n throw new TypeError(\n 'Crop action must be an object of shape { originX: number; originY: number; width: number; height: number }'\n );\n }\n}\n\nfunction validateFlipAction(action: ActionFlip): void {\n if (\n typeof action.flip !== 'string' ||\n ![FlipType.Horizontal, FlipType.Vertical].includes(action.flip)\n ) {\n throw new TypeError(`Unsupported flip type: ${action.flip}`);\n }\n}\n\nfunction validateRotateAction(action: ActionRotate): void {\n if (typeof action.rotate !== 'number') {\n throw new TypeError('Rotation must be a number');\n }\n}\n\nfunction validateResizeAction(action: ActionResize): void {\n const isValid =\n typeof action.resize === 'object' &&\n action.resize !== null &&\n (typeof action.resize.width === 'number' || typeof action.resize.width === 'undefined') &&\n (typeof action.resize.height === 'number' || typeof action.resize.height === 'undefined');\n if (!isValid) {\n throw new TypeError(\n 'Resize action must be an object of shape { width?: number; height?: number }'\n );\n }\n}\n\nexport function validateSaveOptions({ base64, compress, format }: SaveOptions): void {\n if (base64 !== undefined && typeof base64 !== 'boolean') {\n throw new TypeError('The \"base64\" argument must be a boolean');\n }\n if (compress !== undefined) {\n if (typeof compress !== 'number') {\n throw new TypeError('The \"compress\" argument must be a number');\n }\n if (compress < 0 || compress > 1) {\n throw new TypeError('The \"compress\" argument must be a number between 0 and 1');\n }\n }\n const allowedFormats = [SaveFormat.JPEG, SaveFormat.PNG, SaveFormat.WEBP];\n if (format !== undefined && !allowedFormats.includes(format)) {\n throw new TypeError(`The \"format\" argument must be one of: ${allowedFormats.join(', ')}`);\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"validators.js","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Action,\n ActionCrop,\n ActionExtent,\n ActionFlip,\n ActionResize,\n ActionRotate,\n FlipType,\n SaveFormat,\n SaveOptions,\n} from './ImageManipulator.types';\n\nexport function validateArguments(uri: string, actions: Action[], saveOptions: SaveOptions) {\n validateUri(uri);\n validateActions(actions);\n validateSaveOptions(saveOptions);\n}\n\nexport function validateUri(uri: string): void {\n if (!(typeof uri === 'string')) {\n throw new TypeError('The \"uri\" argument must be a string');\n }\n}\n\nexport function validateActions(actions: Action[]): void {\n if (!Array.isArray(actions)) {\n throw new TypeError('The \"actions\" argument must be an array');\n }\n for (const action of actions) {\n if (typeof action !== 'object' || action === null) {\n throw new TypeError('Action must be an object');\n }\n const supportedActionTypes = ['crop', 'extent', 'flip', 'rotate', 'resize'];\n const actionKeys = Object.keys(action);\n if (actionKeys.length !== 1) {\n throw new TypeError(\n `Single action must contain exactly one transformation: ${supportedActionTypes.join(', ')}`\n );\n }\n const actionType = actionKeys[0];\n if (!supportedActionTypes.includes(actionType)) {\n throw new TypeError(`Unsupported action type: ${actionType}`);\n }\n\n if (actionType === 'crop') {\n validateCropAction(action as ActionCrop);\n } else if (actionType === 'extent') {\n validateExtentAction(action as ActionExtent);\n } else if (actionType === 'flip') {\n validateFlipAction(action as ActionFlip);\n } else if (actionType === 'rotate') {\n validateRotateAction(action as ActionRotate);\n } else if (actionType === 'resize') {\n validateResizeAction(action as ActionResize);\n }\n }\n}\n\nfunction validateCropAction(action: ActionCrop): void {\n const isValid =\n typeof action.crop === 'object' &&\n action.crop !== null &&\n typeof action.crop.originX === 'number' &&\n typeof action.crop.originY === 'number' &&\n typeof action.crop.width === 'number' &&\n typeof action.crop.height === 'number';\n if (!isValid) {\n throw new TypeError(\n 'Crop action must be an object of shape { originX: number; originY: number; width: number; height: number }'\n );\n }\n}\n\nfunction validateExtentAction(action: ActionExtent): void {\n const isValid =\n typeof action.extent === 'object' &&\n action.extent !== null &&\n (action.extent.backgroundColor == null || typeof action.extent.backgroundColor === 'string') &&\n (action.extent.originX == null || typeof action.extent.originX === 'number') &&\n (action.extent.originY == null || typeof action.extent.originY === 'number') &&\n typeof action.extent.width === 'number' &&\n typeof action.extent.height === 'number';\n if (!isValid) {\n throw new TypeError(\n 'Extent action must be an object of shape { backgroundColor?: string; originX?: number; originY?: number; width: number; height: number }'\n );\n }\n}\n\nfunction validateFlipAction(action: ActionFlip): void {\n if (\n typeof action.flip !== 'string' ||\n ![FlipType.Horizontal, FlipType.Vertical].includes(action.flip)\n ) {\n throw new TypeError(`Unsupported flip type: ${action.flip}`);\n }\n}\n\nfunction validateRotateAction(action: ActionRotate): void {\n if (typeof action.rotate !== 'number') {\n throw new TypeError('Rotation must be a number');\n }\n}\n\nfunction validateResizeAction(action: ActionResize): void {\n const isValid =\n typeof action.resize === 'object' &&\n action.resize !== null &&\n (typeof action.resize.width === 'number' || typeof action.resize.width === 'undefined') &&\n (typeof action.resize.height === 'number' || typeof action.resize.height === 'undefined');\n if (!isValid) {\n throw new TypeError(\n 'Resize action must be an object of shape { width?: number; height?: number }'\n );\n }\n}\n\nexport function validateSaveOptions({ base64, compress, format }: SaveOptions): void {\n if (base64 !== undefined && typeof base64 !== 'boolean') {\n throw new TypeError('The \"base64\" argument must be a boolean');\n }\n if (compress !== undefined) {\n if (typeof compress !== 'number') {\n throw new TypeError('The \"compress\" argument must be a number');\n }\n if (compress < 0 || compress > 1) {\n throw new TypeError('The \"compress\" argument must be a number between 0 and 1');\n }\n }\n const allowedFormats = [SaveFormat.JPEG, SaveFormat.PNG, SaveFormat.WEBP];\n if (format !== undefined && !allowedFormats.includes(format)) {\n throw new TypeError(`The \"format\" argument must be one of: ${allowedFormats.join(', ')}`);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/expo-image-manipulator/src/ExpoImageManipulator.web.ts b/packages/expo-image-manipulator/src/ExpoImageManipulator.web.ts index 6eecc6f8f3e38..0bd8cee4bdc2b 100644 --- a/packages/expo-image-manipulator/src/ExpoImageManipulator.web.ts +++ b/packages/expo-image-manipulator/src/ExpoImageManipulator.web.ts @@ -1,5 +1,5 @@ import { ImageResult, SaveOptions, Action } from './ImageManipulator.types'; -import { crop, flip, resize, rotate } from './actions/index.web'; +import { crop, extent, flip, resize, rotate } from './actions/index.web'; import { getContext } from './utils/getContext.web'; function getResults(canvas: HTMLCanvasElement, options?: SaveOptions): ImageResult { @@ -56,6 +56,8 @@ export default { const resultCanvas = actions.reduce((canvas, action) => { if ('crop' in action) { return crop(canvas, action.crop); + } else if ('extent' in action) { + return extent(canvas, action.extent); } else if ('resize' in action) { return resize(canvas, action.resize); } else if ('flip' in action) { diff --git a/packages/expo-image-manipulator/src/ImageManipulator.types.ts b/packages/expo-image-manipulator/src/ImageManipulator.types.ts index 32ffbfba99a1c..6a17cf1ce099e 100644 --- a/packages/expo-image-manipulator/src/ImageManipulator.types.ts +++ b/packages/expo-image-manipulator/src/ImageManipulator.types.ts @@ -70,8 +70,26 @@ export type ActionCrop = { }; }; +// @needsAudit +export type ActionExtent = { + /** + * **Set the image size and offset** + * + * If the image is enlarged, unfilled areas are set to the `backgroundColor`. To position the image, use `originX` and `originY`. + * + * @platform web + */ + extent: { + backgroundColor?: string | null; + originX?: number; + originY?: number; + width: number; + height: number; + }; +}; + // @docsMissing -export type Action = ActionResize | ActionRotate | ActionFlip | ActionCrop; +export type Action = ActionResize | ActionRotate | ActionFlip | ActionCrop | ActionExtent; // @docsMissing export enum SaveFormat { diff --git a/packages/expo-image-manipulator/src/__tests__/validators-test.ts b/packages/expo-image-manipulator/src/__tests__/validators-test.ts index d47356c0f9ffd..b02b5f55422a2 100644 --- a/packages/expo-image-manipulator/src/__tests__/validators-test.ts +++ b/packages/expo-image-manipulator/src/__tests__/validators-test.ts @@ -1,5 +1,6 @@ import { ActionCrop, + ActionExtent, ActionFlip, ActionResize, ActionRotate, @@ -65,6 +66,36 @@ describe(validateActions, () => { }); }); + describe('extent', () => { + test('invalid', () => { + expect(() => { + const action = { + extent: { + originY: 10, + width: true, + height: 'blah', + }, + } as unknown as ActionExtent; + validateActions([action]); + }).toThrow(/Extent action must be an object of shape/); + }); + + test('valid', () => { + expect(() => { + validateActions([ + { + extent: { + originX: 10, + originY: 10, + width: 100, + height: 100, + }, + }, + ]); + }).not.toThrow(); + }); + }); + describe('flip', () => { test('invalid', () => { expect(() => { diff --git a/packages/expo-image-manipulator/src/actions/ExtentAction.web.ts b/packages/expo-image-manipulator/src/actions/ExtentAction.web.ts new file mode 100644 index 0000000000000..8f709d3e06453 --- /dev/null +++ b/packages/expo-image-manipulator/src/actions/ExtentAction.web.ts @@ -0,0 +1,43 @@ +import { CodedError } from 'expo-modules-core'; + +import { ActionExtent } from '../ImageManipulator.types'; +import { getContext } from '../utils/getContext.web'; + +export default (canvas: HTMLCanvasElement, options: ActionExtent['extent']) => { + // ensure values are defined. + const { backgroundColor = null, originX = 0, originY = 0, width = 0, height = 0 } = options; + + if (width === 0 || height === 0) { + throw new CodedError( + 'ERR_IMAGE_MANIPULATOR_EXTENT', + 'Extent size must be greater than 0: ' + JSON.stringify(options, null, 2) + ); + } + + const result = document.createElement('canvas'); + result.width = width; + result.height = height; + + const sx = originX < 0 ? 0 : originX; + const sy = originY < 0 ? 0 : originY; + const sw = + originX < 0 ? Math.min(canvas.width, width + originX) : Math.min(canvas.width - originX, width); + const sh = + originY < 0 + ? Math.min(canvas.height, height + originY) + : Math.min(canvas.height - originY, height); + + const dx = originX < 0 ? -originX : 0; + const dy = originY < 0 ? -originY : 0; + + const context = getContext(result); + + if (backgroundColor != null) { + context.fillStyle = backgroundColor; + context.fillRect(0, 0, width, height); + } + + context.drawImage(canvas, sx, sy, sw, sh, dx, dy, sw, sh); + + return result; +}; diff --git a/packages/expo-image-manipulator/src/actions/index.web.ts b/packages/expo-image-manipulator/src/actions/index.web.ts index 904c03bc2cb91..26bdc04c30e15 100644 --- a/packages/expo-image-manipulator/src/actions/index.web.ts +++ b/packages/expo-image-manipulator/src/actions/index.web.ts @@ -1,4 +1,5 @@ export { default as crop } from './CropAction.web'; +export { default as extent } from './ExtentAction.web'; export { default as flip } from './FlipAction.web'; export { default as resize } from './ResizeAction.web'; export { default as rotate } from './RotateAction.web'; diff --git a/packages/expo-image-manipulator/src/validators.ts b/packages/expo-image-manipulator/src/validators.ts index 1fe2a4564d43a..3f5cad2df855a 100644 --- a/packages/expo-image-manipulator/src/validators.ts +++ b/packages/expo-image-manipulator/src/validators.ts @@ -1,6 +1,7 @@ import { Action, ActionCrop, + ActionExtent, ActionFlip, ActionResize, ActionRotate, @@ -29,7 +30,7 @@ export function validateActions(actions: Action[]): void { if (typeof action !== 'object' || action === null) { throw new TypeError('Action must be an object'); } - const supportedActionTypes = ['crop', 'flip', 'rotate', 'resize']; + const supportedActionTypes = ['crop', 'extent', 'flip', 'rotate', 'resize']; const actionKeys = Object.keys(action); if (actionKeys.length !== 1) { throw new TypeError( @@ -43,6 +44,8 @@ export function validateActions(actions: Action[]): void { if (actionType === 'crop') { validateCropAction(action as ActionCrop); + } else if (actionType === 'extent') { + validateExtentAction(action as ActionExtent); } else if (actionType === 'flip') { validateFlipAction(action as ActionFlip); } else if (actionType === 'rotate') { @@ -68,6 +71,22 @@ function validateCropAction(action: ActionCrop): void { } } +function validateExtentAction(action: ActionExtent): void { + const isValid = + typeof action.extent === 'object' && + action.extent !== null && + (action.extent.backgroundColor == null || typeof action.extent.backgroundColor === 'string') && + (action.extent.originX == null || typeof action.extent.originX === 'number') && + (action.extent.originY == null || typeof action.extent.originY === 'number') && + typeof action.extent.width === 'number' && + typeof action.extent.height === 'number'; + if (!isValid) { + throw new TypeError( + 'Extent action must be an object of shape { backgroundColor?: string; originX?: number; originY?: number; width: number; height: number }' + ); + } +} + function validateFlipAction(action: ActionFlip): void { if ( typeof action.flip !== 'string' ||