Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Jimp image operations #506

Merged
merged 28 commits into from
Mar 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
91f4681
Add rotate image operation
j433866 Feb 19, 2019
57e1061
Add Scale Image operation
j433866 Feb 19, 2019
eb8725a
Fix degrees error
j433866 Feb 19, 2019
1a2c5a9
Add resize image operation
j433866 Feb 19, 2019
01acefe
Remove scale image operation.
j433866 Feb 19, 2019
b691c30
Add dither image operation
j433866 Feb 20, 2019
74c2a2b
Add Invert Image operation
j433866 Feb 20, 2019
a0b94bb
Change run() functions to be async
j433866 Feb 20, 2019
0dd4304
Add new Blur Image operation.
j433866 Feb 20, 2019
fd160e8
Add image operations to Categories
j433866 Feb 20, 2019
da838e2
Add flip image operation
j433866 Feb 20, 2019
9f4aa0a
Remove trailing space
j433866 Feb 20, 2019
0d86a7e
Add resize algorithm option
j433866 Feb 20, 2019
7975fad
Add options for min, max and step values for number inputs.
j433866 Mar 4, 2019
7b6062a
Set min blur amount to 1, add status message for gaussian blur.
j433866 Mar 4, 2019
d09e608
Add min width and height values
j433866 Mar 4, 2019
f281a32
Add Wikipedia URLs
j433866 Mar 4, 2019
588a8b2
Fix code syntax
j433866 Mar 4, 2019
4f1a897
Add Crop Image operation
j433866 Mar 4, 2019
737ce99
Add image brightness / contrast operation
j433866 Mar 4, 2019
ec1fd7b
Add image opacity operation
j433866 Mar 4, 2019
514eef5
Add image filter operation
j433866 Mar 4, 2019
370ae32
Fix linting
j433866 Mar 5, 2019
662922b
Add resizing status message
j433866 Mar 6, 2019
833c1cd
Add Contain Image, Cover Image and Image Hue / Saturation / Lightness…
j433866 Mar 7, 2019
4a7ea46
Add status messages for image operations
j433866 Mar 7, 2019
1031429
Add error handling
j433866 Mar 7, 2019
0c9db5a
Fix typo
j433866 Mar 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/core/Ingredient.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class Ingredient {
this.toggleValues = [];
this.target = null;
this.defaultIndex = 0;
this.min = null;
this.max = null;
this.step = 1;

if (ingredientConfig) {
this._parseConfig(ingredientConfig);
Expand All @@ -50,6 +53,9 @@ class Ingredient {
this.toggleValues = ingredientConfig.toggleValues;
this.target = typeof ingredientConfig.target !== "undefined" ? ingredientConfig.target : null;
this.defaultIndex = typeof ingredientConfig.defaultIndex !== "undefined" ? ingredientConfig.defaultIndex : 0;
this.min = ingredientConfig.min;
this.max = ingredientConfig.max;
this.step = ingredientConfig.step;
}


Expand Down
3 changes: 3 additions & 0 deletions src/core/Operation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ class Operation {
if (ing.disabled) conf.disabled = ing.disabled;
if (ing.target) conf.target = ing.target;
if (ing.defaultIndex) conf.defaultIndex = ing.defaultIndex;
if (typeof ing.min === "number") conf.min = ing.min;
if (typeof ing.max === "number") conf.max = ing.max;
if (ing.step) conf.step = ing.step;
return conf;
});
}
Expand Down
15 changes: 14 additions & 1 deletion src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,20 @@
"Play Media",
"Remove EXIF",
"Extract EXIF",
"Split Colour Channels"
"Split Colour Channels",
"Rotate Image",
"Resize Image",
"Blur Image",
"Dither Image",
"Invert Image",
"Flip Image",
"Crop Image",
"Image Brightness / Contrast",
"Image Opacity",
"Image Filter",
"Contain Image",
"Cover Image",
"Image Hue/Saturation/Lightness"
]
},
{
Expand Down
107 changes: 107 additions & 0 deletions src/core/operations/BlurImage.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/

import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Magic from "../lib/Magic";
import { toBase64 } from "../lib/Base64";
import jimp from "jimp";

/**
* Blur Image operation
*/
class BlurImage extends Operation {

/**
* BlurImage constructor
*/
constructor() {
super();

this.name = "Blur Image";
this.module = "Image";
this.description = "Applies a blur effect to the image.<br><br>Gaussian blur is much slower than fast blur, but produces better results.";
this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.presentType = "html";
this.args = [
{
name: "Blur Amount",
type: "number",
value: 5,
min: 1
},
{
name: "Blur Type",
type: "option",
value: ["Fast", "Gaussian"]
}
];
}

/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const [blurAmount, blurType] = args;
const type = Magic.magicFileType(input);

if (type && type.mime.indexOf("image") === 0){
let image;
try {
image = await jimp.read(Buffer.from(input));
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
switch (blurType){
case "Fast":
image.blur(blurAmount);
break;
case "Gaussian":
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage("Gaussian blurring image. This will take a while...");
image.gaussian(blurAmount);
break;
}

const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
} catch (err) {
throw new OperationError(`Error blurring image. (${err})`);
}
} else {
throw new OperationError("Invalid file type.");
}
}

/**
* Displays the blurred image using HTML for web apps
* @param {byteArray} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";

let dataURI = "data:";
const type = Magic.magicFileType(data);
if (type && type.mime.indexOf("image") === 0){
dataURI += type.mime + ";";
} else {
throw new OperationError("Invalid file type.");
}
dataURI += "base64," + toBase64(data);

return "<img src='" + dataURI + "'>";

}

}

export default BlurImage;
148 changes: 148 additions & 0 deletions src/core/operations/ContainImage.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/

import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Magic from "../lib/Magic";
import { toBase64 } from "../lib/Base64.mjs";
import jimp from "jimp";

/**
* Contain Image operation
*/
class ContainImage extends Operation {

/**
* ContainImage constructor
*/
constructor() {
super();

this.name = "Contain Image";
this.module = "Image";
this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed.";
this.infoURL = "";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.presentType = "html";
this.args = [
{
name: "Width",
type: "number",
value: 100,
min: 1
},
{
name: "Height",
type: "number",
value: 100,
min: 1
},
{
name: "Horizontal align",
type: "option",
value: [
"Left",
"Center",
"Right"
],
defaultIndex: 1
},
{
name: "Vertical align",
type: "option",
value: [
"Top",
"Middle",
"Bottom"
],
defaultIndex: 1
},
{
name: "Resizing algorithm",
type: "option",
value: [
"Nearest Neighbour",
"Bilinear",
"Bicubic",
"Hermite",
"Bezier"
],
defaultIndex: 1
}
];
}

/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const [width, height, hAlign, vAlign, alg] = args;
const type = Magic.magicFileType(input);

const resizeMap = {
"Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR,
"Bilinear": jimp.RESIZE_BILINEAR,
"Bicubic": jimp.RESIZE_BICUBIC,
"Hermite": jimp.RESIZE_HERMITE,
"Bezier": jimp.RESIZE_BEZIER
};

const alignMap = {
"Left": jimp.HORIZONTAL_ALIGN_LEFT,
"Center": jimp.HORIZONTAL_ALIGN_CENTER,
"Right": jimp.HORIZONTAL_ALIGN_RIGHT,
"Top": jimp.VERTICAL_ALIGN_TOP,
"Middle": jimp.VERTICAL_ALIGN_MIDDLE,
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
};

if (!type || type.mime.indexOf("image") !== 0){
throw new OperationError("Invalid file type.");
}

let image;
try {
image = await jimp.read(Buffer.from(input));
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage("Containing image...");
image.contain(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
} catch (err) {
throw new OperationError(`Error containing image. (${err})`);
}
}

/**
* Displays the contained image using HTML for web apps
* @param {byteArray} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";

let dataURI = "data:";
const type = Magic.magicFileType(data);
if (type && type.mime.indexOf("image") === 0){
dataURI += type.mime + ";";
} else {
throw new OperationError("Invalid file type");
}
dataURI += "base64," + toBase64(data);

return "<img src='" + dataURI + "'>";
}

}

export default ContainImage;
Loading