cyberchef 10.22.0 → 10.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +131 -0
- package/CONTRIBUTING.md +37 -0
- package/Dockerfile +2 -0
- package/Gruntfile.js +0 -12
- package/README.md +1 -1
- package/babel.config.js +0 -6
- package/package.json +63 -62
- package/src/core/Chef.mjs +8 -1
- package/src/core/Ingredient.mjs +5 -2
- package/src/core/Operation.mjs +6 -1
- package/src/core/Recipe.mjs +10 -5
- package/src/core/config/Categories.json +18 -3
- package/src/core/config/OperationConfig.json +22310 -0
- package/src/core/config/modules/Bletchley.mjs +28 -0
- package/src/core/config/modules/Charts.mjs +24 -0
- package/src/core/config/modules/Ciphers.mjs +128 -0
- package/src/core/config/modules/Code.mjs +60 -0
- package/src/core/config/modules/Compression.mjs +52 -0
- package/src/core/config/modules/Crypto.mjs +134 -0
- package/src/core/config/modules/Default.mjs +424 -0
- package/src/core/config/modules/Diff.mjs +16 -0
- package/src/core/config/modules/Encodings.mjs +42 -0
- package/src/core/config/modules/Handlebars.mjs +16 -0
- package/src/core/config/modules/Hashing.mjs +30 -0
- package/src/core/config/modules/Image.mjs +70 -0
- package/src/core/config/modules/Jq.mjs +16 -0
- package/src/core/config/modules/OCR.mjs +16 -0
- package/src/core/config/modules/OpModules.mjs +63 -0
- package/src/core/config/modules/PGP.mjs +26 -0
- package/src/core/config/modules/Protobuf.mjs +18 -0
- package/src/core/config/modules/PublicKey.mjs +36 -0
- package/src/core/config/modules/Regex.mjs +40 -0
- package/src/core/config/modules/Serialise.mjs +26 -0
- package/src/core/config/modules/Shellcode.mjs +18 -0
- package/src/core/config/modules/URL.mjs +20 -0
- package/src/core/config/modules/UserAgent.mjs +16 -0
- package/src/core/config/modules/Yara.mjs +16 -0
- package/src/core/lib/AudioBytes.mjs +103 -0
- package/src/core/lib/AudioMetaSchema.mjs +82 -0
- package/src/core/lib/AudioParsers.mjs +630 -0
- package/src/core/lib/BigIntUtils.mjs +73 -0
- package/src/core/lib/Modhex.mjs +2 -0
- package/src/core/lib/QRCode.mjs +30 -10
- package/src/core/lib/RC6.mjs +625 -0
- package/src/core/operations/A1Z26CipherDecode.mjs +1 -1
- package/src/core/operations/AddTextToImage.mjs +116 -64
- package/src/core/operations/BlurImage.mjs +10 -12
- package/src/core/operations/ContainImage.mjs +50 -40
- package/src/core/operations/ConvertImageFormat.mjs +33 -39
- package/src/core/operations/CoverImage.mjs +39 -37
- package/src/core/operations/CropImage.mjs +35 -21
- package/src/core/operations/DisassembleARM.mjs +193 -0
- package/src/core/operations/DitherImage.mjs +8 -8
- package/src/core/operations/EscapeUnicodeCharacters.mjs +0 -17
- package/src/core/operations/ExtractAudioMetadata.mjs +175 -0
- package/src/core/operations/ExtractLSB.mjs +17 -11
- package/src/core/operations/ExtractRGBA.mjs +12 -10
- package/src/core/operations/FlaskSessionDecode.mjs +80 -0
- package/src/core/operations/FlaskSessionSign.mjs +89 -0
- package/src/core/operations/FlaskSessionVerify.mjs +136 -0
- package/src/core/operations/FlipImage.mjs +14 -10
- package/src/core/operations/GenerateImage.mjs +39 -32
- package/src/core/operations/ImageBrightnessContrast.mjs +10 -10
- package/src/core/operations/ImageFilter.mjs +14 -13
- package/src/core/operations/ImageHueSaturationLightness.mjs +22 -20
- package/src/core/operations/ImageOpacity.mjs +6 -8
- package/src/core/operations/InvertImage.mjs +4 -6
- package/src/core/operations/Jq.mjs +12 -4
- package/src/core/operations/NormaliseImage.mjs +5 -7
- package/src/core/operations/OffsetChecker.mjs +1 -1
- package/src/core/operations/ParseEthernetFrame.mjs +112 -0
- package/src/core/operations/ParseIPv4Header.mjs +23 -6
- package/src/core/operations/ParseQRCode.mjs +13 -13
- package/src/core/operations/PseudoRandomIntegerGenerator.mjs +164 -0
- package/src/core/operations/RC6Decrypt.mjs +119 -0
- package/src/core/operations/RC6Encrypt.mjs +119 -0
- package/src/core/operations/RandomizeColourPalette.mjs +11 -11
- package/src/core/operations/ResizeImage.mjs +30 -23
- package/src/core/operations/RotateImage.mjs +8 -9
- package/src/core/operations/SQLBeautify.mjs +21 -3
- package/src/core/operations/SharpenImage.mjs +94 -62
- package/src/core/operations/SplitColourChannels.mjs +47 -21
- package/src/core/operations/TextIntegerConverter.mjs +123 -0
- package/src/core/operations/UnescapeUnicodeCharacters.mjs +17 -0
- package/src/core/operations/ViewBitPlane.mjs +16 -20
- package/src/core/operations/index.mjs +960 -0
- package/src/node/index.mjs +2423 -0
- package/src/web/HTMLIngredient.mjs +24 -43
- package/src/web/Manager.mjs +1 -0
- package/src/web/html/index.html +6 -6
- package/src/web/static/fonts/bmfonts/Roboto72White.fnt +491 -485
- package/src/web/static/fonts/bmfonts/RobotoBlack72White.fnt +494 -488
- package/src/web/static/fonts/bmfonts/RobotoMono72White.fnt +110 -103
- package/src/web/static/fonts/bmfonts/RobotoSlab72White.fnt +498 -492
- package/src/web/stylesheets/layout/_banner.css +30 -0
- package/src/web/stylesheets/layout/_modals.css +5 -0
- package/src/web/stylesheets/utils/_overrides.css +7 -0
- package/src/web/waiters/ControlsWaiter.mjs +82 -0
- package/src/web/waiters/InputWaiter.mjs +12 -6
- package/src/web/waiters/RecipeWaiter.mjs +2 -2
- package/tests/browser/02_ops.js +23 -3
- package/tests/node/index.mjs +1 -0
- package/tests/node/tests/lib/BigIntUtils.mjs +150 -0
- package/tests/node/tests/operations.mjs +9 -7
- package/tests/operations/index.mjs +8 -0
- package/tests/operations/tests/A1Z26CipherDecode.mjs +33 -0
- package/tests/operations/tests/DisassembleARM.mjs +377 -0
- package/tests/operations/tests/ExtractAudioMetadata.mjs +287 -0
- package/tests/operations/tests/FlaskSession.mjs +246 -0
- package/tests/operations/tests/GenerateQRCode.mjs +67 -0
- package/tests/operations/tests/JWTSign.mjs +83 -8
- package/tests/operations/tests/Jq.mjs +32 -0
- package/tests/operations/tests/Modhex.mjs +20 -0
- package/tests/operations/tests/ParseEthernetFrame.mjs +45 -0
- package/tests/operations/tests/RC6.mjs +487 -0
- package/tests/operations/tests/SQLBeautify.mjs +54 -0
- package/tests/operations/tests/TextIntegerConverter.mjs +199 -0
- package/tests/samples/Audio.mjs +73 -0
- package/tests/samples/Images.mjs +0 -12
- package/webpack.config.js +10 -7
- package/src/core/lib/ImageManipulation.mjs +0 -251
|
@@ -10,13 +10,12 @@ import Utils from "../Utils.mjs";
|
|
|
10
10
|
import { isImage } from "../lib/FileType.mjs";
|
|
11
11
|
import { runHash } from "../lib/Hash.mjs";
|
|
12
12
|
import { toBase64 } from "../lib/Base64.mjs";
|
|
13
|
-
import Jimp from "jimp
|
|
13
|
+
import { Jimp } from "jimp";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Randomize Colour Palette operation
|
|
17
17
|
*/
|
|
18
18
|
class RandomizeColourPalette extends Operation {
|
|
19
|
-
|
|
20
19
|
/**
|
|
21
20
|
* RandomizeColourPalette constructor
|
|
22
21
|
*/
|
|
@@ -25,7 +24,8 @@ class RandomizeColourPalette extends Operation {
|
|
|
25
24
|
|
|
26
25
|
this.name = "Randomize Colour Palette";
|
|
27
26
|
this.module = "Image";
|
|
28
|
-
this.description =
|
|
27
|
+
this.description =
|
|
28
|
+
"Randomizes each colour in an image's colour palette. This can often reveal text or symbols that were previously a very similar colour to their surroundings, a technique sometimes used in Steganography.";
|
|
29
29
|
this.infoURL = "https://wikipedia.org/wiki/Indexed_color";
|
|
30
30
|
this.inputType = "ArrayBuffer";
|
|
31
31
|
this.outputType = "ArrayBuffer";
|
|
@@ -34,8 +34,8 @@ class RandomizeColourPalette extends Operation {
|
|
|
34
34
|
{
|
|
35
35
|
name: "Seed",
|
|
36
36
|
type: "string",
|
|
37
|
-
value: ""
|
|
38
|
-
}
|
|
37
|
+
value: "",
|
|
38
|
+
},
|
|
39
39
|
];
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -45,23 +45,24 @@ class RandomizeColourPalette extends Operation {
|
|
|
45
45
|
* @returns {ArrayBuffer}
|
|
46
46
|
*/
|
|
47
47
|
async run(input, args) {
|
|
48
|
-
if (!isImage(input))
|
|
48
|
+
if (!isImage(input))
|
|
49
|
+
throw new OperationError("Please enter a valid image file.");
|
|
49
50
|
|
|
50
|
-
const seed = args[0] ||
|
|
51
|
+
const seed = args[0] || Math.random().toString().substr(2),
|
|
51
52
|
parsedImage = await Jimp.read(input),
|
|
52
53
|
width = parsedImage.bitmap.width,
|
|
53
54
|
height = parsedImage.bitmap.height;
|
|
54
55
|
|
|
55
56
|
let rgbString, rgbHash, rgbHex;
|
|
56
57
|
|
|
57
|
-
parsedImage.scan(0, 0, width, height, function(x, y, idx) {
|
|
58
|
-
rgbString = this.bitmap.data.slice(idx, idx+3).join(".");
|
|
58
|
+
parsedImage.scan(0, 0, width, height, function (x, y, idx) {
|
|
59
|
+
rgbString = this.bitmap.data.slice(idx, idx + 3).join(".");
|
|
59
60
|
rgbHash = runHash("md5", Utils.strToArrayBuffer(seed + rgbString));
|
|
60
61
|
rgbHex = rgbHash.substr(0, 6) + "ff";
|
|
61
62
|
parsedImage.setPixelColor(parseInt(rgbHex, 16), x, y);
|
|
62
63
|
});
|
|
63
64
|
|
|
64
|
-
const imageBuffer = await parsedImage.
|
|
65
|
+
const imageBuffer = await parsedImage.getBuffer(parsedImage.mime);
|
|
65
66
|
|
|
66
67
|
return new Uint8Array(imageBuffer).buffer;
|
|
67
68
|
}
|
|
@@ -77,7 +78,6 @@ class RandomizeColourPalette extends Operation {
|
|
|
77
78
|
|
|
78
79
|
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
|
79
80
|
}
|
|
80
|
-
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export default RandomizeColourPalette;
|
|
@@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
|
|
|
9
9
|
import { isImage } from "../lib/FileType.mjs";
|
|
10
10
|
import { toBase64 } from "../lib/Base64.mjs";
|
|
11
11
|
import { isWorkerEnvironment } from "../Utils.mjs";
|
|
12
|
-
import Jimp from "jimp
|
|
12
|
+
import { Jimp, JimpMime, ResizeStrategy } from "jimp";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Resize Image operation
|
|
16
16
|
*/
|
|
17
17
|
class ResizeImage extends Operation {
|
|
18
|
-
|
|
19
18
|
/**
|
|
20
19
|
* ResizeImage constructor
|
|
21
20
|
*/
|
|
@@ -24,7 +23,8 @@ class ResizeImage extends Operation {
|
|
|
24
23
|
|
|
25
24
|
this.name = "Resize Image";
|
|
26
25
|
this.module = "Image";
|
|
27
|
-
this.description =
|
|
26
|
+
this.description =
|
|
27
|
+
"Resizes an image to the specified width and height values.";
|
|
28
28
|
this.infoURL = "https://wikipedia.org/wiki/Image_scaling";
|
|
29
29
|
this.inputType = "ArrayBuffer";
|
|
30
30
|
this.outputType = "ArrayBuffer";
|
|
@@ -34,23 +34,23 @@ class ResizeImage extends Operation {
|
|
|
34
34
|
name: "Width",
|
|
35
35
|
type: "number",
|
|
36
36
|
value: 100,
|
|
37
|
-
min: 1
|
|
37
|
+
min: 1,
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
name: "Height",
|
|
41
41
|
type: "number",
|
|
42
42
|
value: 100,
|
|
43
|
-
min: 1
|
|
43
|
+
min: 1,
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
name: "Unit type",
|
|
47
47
|
type: "option",
|
|
48
|
-
value: ["Pixels", "Percent"]
|
|
48
|
+
value: ["Pixels", "Percent"],
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
name: "Maintain aspect ratio",
|
|
52
52
|
type: "boolean",
|
|
53
|
-
value: false
|
|
53
|
+
value: false,
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
name: "Resizing algorithm",
|
|
@@ -60,10 +60,10 @@ class ResizeImage extends Operation {
|
|
|
60
60
|
"Bilinear",
|
|
61
61
|
"Bicubic",
|
|
62
62
|
"Hermite",
|
|
63
|
-
"Bezier"
|
|
63
|
+
"Bezier",
|
|
64
64
|
],
|
|
65
|
-
defaultIndex: 1
|
|
66
|
-
}
|
|
65
|
+
defaultIndex: 1,
|
|
66
|
+
},
|
|
67
67
|
];
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -80,11 +80,11 @@ class ResizeImage extends Operation {
|
|
|
80
80
|
resizeAlg = args[4];
|
|
81
81
|
|
|
82
82
|
const resizeMap = {
|
|
83
|
-
"Nearest Neighbour":
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
"Nearest Neighbour": ResizeStrategy.NEAREST_NEIGHBOR,
|
|
84
|
+
Bilinear: ResizeStrategy.BILINEAR,
|
|
85
|
+
Bicubic: ResizeStrategy.BICUBIC,
|
|
86
|
+
Hermite: ResizeStrategy.HERMITE,
|
|
87
|
+
Bezier: ResizeStrategy.BEZIER,
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
if (!isImage(input)) {
|
|
@@ -99,23 +99,31 @@ class ResizeImage extends Operation {
|
|
|
99
99
|
}
|
|
100
100
|
try {
|
|
101
101
|
if (unit === "Percent") {
|
|
102
|
-
width = image.
|
|
103
|
-
height = image.
|
|
102
|
+
width = image.width * (width / 100);
|
|
103
|
+
height = image.height * (height / 100);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
if (isWorkerEnvironment())
|
|
107
107
|
self.sendStatusMessage("Resizing image...");
|
|
108
108
|
if (aspect) {
|
|
109
|
-
image.scaleToFit(
|
|
109
|
+
image.scaleToFit({
|
|
110
|
+
w: width,
|
|
111
|
+
h: height,
|
|
112
|
+
mode: resizeMap[resizeAlg],
|
|
113
|
+
});
|
|
110
114
|
} else {
|
|
111
|
-
image.resize(
|
|
115
|
+
image.resize({
|
|
116
|
+
w: width,
|
|
117
|
+
h: height,
|
|
118
|
+
mode: resizeMap[resizeAlg],
|
|
119
|
+
});
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
let imageBuffer;
|
|
115
|
-
if (image.
|
|
116
|
-
imageBuffer = await image.
|
|
123
|
+
if (image.mime === "image/gif") {
|
|
124
|
+
imageBuffer = await image.getBuffer(JimpMime.png);
|
|
117
125
|
} else {
|
|
118
|
-
imageBuffer = await image.
|
|
126
|
+
imageBuffer = await image.getBuffer(image.mime);
|
|
119
127
|
}
|
|
120
128
|
return imageBuffer.buffer;
|
|
121
129
|
} catch (err) {
|
|
@@ -139,7 +147,6 @@ class ResizeImage extends Operation {
|
|
|
139
147
|
|
|
140
148
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
|
141
149
|
}
|
|
142
|
-
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
export default ResizeImage;
|
|
@@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
|
|
|
9
9
|
import { isImage } from "../lib/FileType.mjs";
|
|
10
10
|
import { toBase64 } from "../lib/Base64.mjs";
|
|
11
11
|
import { isWorkerEnvironment } from "../Utils.mjs";
|
|
12
|
-
import Jimp from "jimp
|
|
12
|
+
import { Jimp, JimpMime } from "jimp";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Rotate Image operation
|
|
16
16
|
*/
|
|
17
17
|
class RotateImage extends Operation {
|
|
18
|
-
|
|
19
18
|
/**
|
|
20
19
|
* RotateImage constructor
|
|
21
20
|
*/
|
|
@@ -24,7 +23,8 @@ class RotateImage extends Operation {
|
|
|
24
23
|
|
|
25
24
|
this.name = "Rotate Image";
|
|
26
25
|
this.module = "Image";
|
|
27
|
-
this.description =
|
|
26
|
+
this.description =
|
|
27
|
+
"Rotates an image by the specified number of degrees.";
|
|
28
28
|
this.infoURL = "";
|
|
29
29
|
this.inputType = "ArrayBuffer";
|
|
30
30
|
this.outputType = "ArrayBuffer";
|
|
@@ -33,8 +33,8 @@ class RotateImage extends Operation {
|
|
|
33
33
|
{
|
|
34
34
|
name: "Rotation amount (degrees)",
|
|
35
35
|
type: "number",
|
|
36
|
-
value: 90
|
|
37
|
-
}
|
|
36
|
+
value: 90,
|
|
37
|
+
},
|
|
38
38
|
];
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -62,10 +62,10 @@ class RotateImage extends Operation {
|
|
|
62
62
|
image.rotate(degrees);
|
|
63
63
|
|
|
64
64
|
let imageBuffer;
|
|
65
|
-
if (image.
|
|
66
|
-
imageBuffer = await image.
|
|
65
|
+
if (image.mime === "image/gif") {
|
|
66
|
+
imageBuffer = await image.getBuffer(JimpMime.png);
|
|
67
67
|
} else {
|
|
68
|
-
imageBuffer = await image.
|
|
68
|
+
imageBuffer = await image.getBuffer(image.mime);
|
|
69
69
|
}
|
|
70
70
|
return imageBuffer.buffer;
|
|
71
71
|
} catch (err) {
|
|
@@ -89,7 +89,6 @@ class RotateImage extends Operation {
|
|
|
89
89
|
|
|
90
90
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
|
91
91
|
}
|
|
92
|
-
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
export default RotateImage;
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* @copyright Crown Copyright 2016
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
import vkbeautify from "vkbeautify";
|
|
6
|
+
import { format } from "sql-formatter";
|
|
8
7
|
import Operation from "../Operation.mjs";
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -39,7 +38,26 @@ class SQLBeautify extends Operation {
|
|
|
39
38
|
*/
|
|
40
39
|
run(input, args) {
|
|
41
40
|
const indentStr = args[0];
|
|
42
|
-
|
|
41
|
+
// Extract and replace bind variables like :Bind1 with __BIND_0__
|
|
42
|
+
const bindRegex = /:\w+/g;
|
|
43
|
+
const bindMap = {};
|
|
44
|
+
let bindCounter=0;
|
|
45
|
+
const placeholderInput = input.replace(bindRegex, (match) => {
|
|
46
|
+
const placeholder = `__BIND_${bindCounter++}__`;
|
|
47
|
+
bindMap[placeholder] = match;
|
|
48
|
+
return placeholder;
|
|
49
|
+
});
|
|
50
|
+
// Format the SQL with chosen options
|
|
51
|
+
let formatted= format(placeholderInput, {
|
|
52
|
+
language: "mysql", // Use MySQL as the default dialect for better compatibility with real-world SQL
|
|
53
|
+
useTabs: indentStr==="\t", // true if tab, false if spaces
|
|
54
|
+
tabWidth: indentStr.length || 4, // fallback if empty
|
|
55
|
+
indentStyle: "standard" // fine for most SQL
|
|
56
|
+
});
|
|
57
|
+
// Replace placeholders back with original bind variables
|
|
58
|
+
formatted = formatted.replace(/__BIND_\d+__/g, match => bindMap[match] || match);
|
|
59
|
+
|
|
60
|
+
return formatted;
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
}
|
|
@@ -8,15 +8,13 @@ import Operation from "../Operation.mjs";
|
|
|
8
8
|
import OperationError from "../errors/OperationError.mjs";
|
|
9
9
|
import { isImage } from "../lib/FileType.mjs";
|
|
10
10
|
import { toBase64 } from "../lib/Base64.mjs";
|
|
11
|
-
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
|
|
12
11
|
import { isWorkerEnvironment } from "../Utils.mjs";
|
|
13
|
-
import Jimp from "jimp
|
|
12
|
+
import { Jimp, JimpMime } from "jimp";
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Sharpen Image operation
|
|
17
16
|
*/
|
|
18
17
|
class SharpenImage extends Operation {
|
|
19
|
-
|
|
20
18
|
/**
|
|
21
19
|
* SharpenImage constructor
|
|
22
20
|
*/
|
|
@@ -35,22 +33,22 @@ class SharpenImage extends Operation {
|
|
|
35
33
|
name: "Radius",
|
|
36
34
|
type: "number",
|
|
37
35
|
value: 2,
|
|
38
|
-
min: 1
|
|
36
|
+
min: 1,
|
|
39
37
|
},
|
|
40
38
|
{
|
|
41
39
|
name: "Amount",
|
|
42
40
|
type: "number",
|
|
43
41
|
value: 1,
|
|
44
42
|
min: 0,
|
|
45
|
-
step: 0.1
|
|
43
|
+
step: 0.1,
|
|
46
44
|
},
|
|
47
45
|
{
|
|
48
46
|
name: "Threshold",
|
|
49
47
|
type: "number",
|
|
50
48
|
value: 10,
|
|
51
49
|
min: 0,
|
|
52
|
-
max: 100
|
|
53
|
-
}
|
|
50
|
+
max: 100,
|
|
51
|
+
},
|
|
54
52
|
];
|
|
55
53
|
}
|
|
56
54
|
|
|
@@ -79,67 +77,102 @@ class SharpenImage extends Operation {
|
|
|
79
77
|
const blurMask = image.clone();
|
|
80
78
|
|
|
81
79
|
if (isWorkerEnvironment())
|
|
82
|
-
self.sendStatusMessage(
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
self.sendStatusMessage(
|
|
81
|
+
"Sharpening image... (Blurring cloned image)",
|
|
82
|
+
);
|
|
83
|
+
const blurImage = image.clone().gaussian(radius);
|
|
85
84
|
|
|
86
85
|
if (isWorkerEnvironment())
|
|
87
|
-
self.sendStatusMessage(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
86
|
+
self.sendStatusMessage(
|
|
87
|
+
"Sharpening image... (Creating unsharp mask)",
|
|
88
|
+
);
|
|
89
|
+
blurMask.scan(
|
|
90
|
+
0,
|
|
91
|
+
0,
|
|
92
|
+
blurMask.bitmap.width,
|
|
93
|
+
blurMask.bitmap.height,
|
|
94
|
+
function (x, y, idx) {
|
|
95
|
+
const blurRed = blurImage.bitmap.data[idx];
|
|
96
|
+
const blurGreen = blurImage.bitmap.data[idx + 1];
|
|
97
|
+
const blurBlue = blurImage.bitmap.data[idx + 2];
|
|
98
|
+
|
|
99
|
+
const normalRed = this.bitmap.data[idx];
|
|
100
|
+
const normalGreen = this.bitmap.data[idx + 1];
|
|
101
|
+
const normalBlue = this.bitmap.data[idx + 2];
|
|
102
|
+
|
|
103
|
+
// Subtract blurred pixel value from normal image
|
|
104
|
+
this.bitmap.data[idx] =
|
|
105
|
+
normalRed > blurRed ? normalRed - blurRed : 0;
|
|
106
|
+
this.bitmap.data[idx + 1] =
|
|
107
|
+
normalGreen > blurGreen ? normalGreen - blurGreen : 0;
|
|
108
|
+
this.bitmap.data[idx + 2] =
|
|
109
|
+
normalBlue > blurBlue ? normalBlue - blurBlue : 0;
|
|
110
|
+
},
|
|
111
|
+
);
|
|
102
112
|
|
|
103
113
|
if (isWorkerEnvironment())
|
|
104
|
-
self.sendStatusMessage(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
114
|
+
self.sendStatusMessage(
|
|
115
|
+
"Sharpening image... (Merging with unsharp mask)",
|
|
116
|
+
);
|
|
117
|
+
image.scan(
|
|
118
|
+
0,
|
|
119
|
+
0,
|
|
120
|
+
image.bitmap.width,
|
|
121
|
+
image.bitmap.height,
|
|
122
|
+
function (x, y, idx) {
|
|
123
|
+
let maskRed = blurMask.bitmap.data[idx];
|
|
124
|
+
let maskGreen = blurMask.bitmap.data[idx + 1];
|
|
125
|
+
let maskBlue = blurMask.bitmap.data[idx + 2];
|
|
126
|
+
|
|
127
|
+
const normalRed = this.bitmap.data[idx];
|
|
128
|
+
const normalGreen = this.bitmap.data[idx + 1];
|
|
129
|
+
const normalBlue = this.bitmap.data[idx + 2];
|
|
130
|
+
|
|
131
|
+
// Calculate luminance
|
|
132
|
+
const maskLuminance =
|
|
133
|
+
0.2126 * maskRed +
|
|
134
|
+
0.7152 * maskGreen +
|
|
135
|
+
0.0722 * maskBlue;
|
|
136
|
+
const normalLuminance =
|
|
137
|
+
0.2126 * normalRed +
|
|
138
|
+
0.7152 * normalGreen +
|
|
139
|
+
0.0722 * normalBlue;
|
|
140
|
+
|
|
141
|
+
let luminanceDiff;
|
|
142
|
+
if (maskLuminance > normalLuminance) {
|
|
143
|
+
luminanceDiff = maskLuminance - normalLuminance;
|
|
144
|
+
} else {
|
|
145
|
+
luminanceDiff = normalLuminance - maskLuminance;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Scale mask colours by amount
|
|
149
|
+
maskRed = maskRed * amount;
|
|
150
|
+
maskGreen = maskGreen * amount;
|
|
151
|
+
maskBlue = maskBlue * amount;
|
|
152
|
+
|
|
153
|
+
// Only change pixel value if the difference is higher than threshold
|
|
154
|
+
if ((luminanceDiff / 255) * 100 >= threshold) {
|
|
155
|
+
this.bitmap.data[idx] =
|
|
156
|
+
normalRed + maskRed <= 255 ?
|
|
157
|
+
normalRed + maskRed :
|
|
158
|
+
255;
|
|
159
|
+
this.bitmap.data[idx + 1] =
|
|
160
|
+
normalGreen + maskGreen <= 255 ?
|
|
161
|
+
normalGreen + maskGreen :
|
|
162
|
+
255;
|
|
163
|
+
this.bitmap.data[idx + 2] =
|
|
164
|
+
normalBlue + maskBlue <= 255 ?
|
|
165
|
+
normalBlue + maskBlue :
|
|
166
|
+
255;
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
);
|
|
137
170
|
|
|
138
171
|
let imageBuffer;
|
|
139
|
-
if (image.
|
|
140
|
-
imageBuffer = await image.
|
|
172
|
+
if (image.mime === "image/gif") {
|
|
173
|
+
imageBuffer = await image.getBuffer(JimpMime.png);
|
|
141
174
|
} else {
|
|
142
|
-
imageBuffer = await image.
|
|
175
|
+
imageBuffer = await image.getBuffer(image.mime);
|
|
143
176
|
}
|
|
144
177
|
return imageBuffer.buffer;
|
|
145
178
|
} catch (err) {
|
|
@@ -163,7 +196,6 @@ class SharpenImage extends Operation {
|
|
|
163
196
|
|
|
164
197
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
|
165
198
|
}
|
|
166
|
-
|
|
167
199
|
}
|
|
168
200
|
|
|
169
201
|
export default SharpenImage;
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
import Operation from "../Operation.mjs";
|
|
8
8
|
import OperationError from "../errors/OperationError.mjs";
|
|
9
9
|
import Utils from "../Utils.mjs";
|
|
10
|
-
import {isImage} from "../lib/FileType.mjs";
|
|
11
|
-
import Jimp from "jimp
|
|
10
|
+
import { isImage } from "../lib/FileType.mjs";
|
|
11
|
+
import { Jimp, JimpMime } from "jimp";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Split Colour Channels operation
|
|
15
15
|
*/
|
|
16
16
|
class SplitColourChannels extends Operation {
|
|
17
|
-
|
|
18
17
|
/**
|
|
19
18
|
* SplitColourChannels constructor
|
|
20
19
|
*/
|
|
@@ -23,7 +22,8 @@ class SplitColourChannels extends Operation {
|
|
|
23
22
|
|
|
24
23
|
this.name = "Split Colour Channels";
|
|
25
24
|
this.module = "Image";
|
|
26
|
-
this.description =
|
|
25
|
+
this.description =
|
|
26
|
+
"Splits the given image into its red, green and blue colour channels.";
|
|
27
27
|
this.infoURL = "https://wikipedia.org/wiki/Channel_(digital_image)";
|
|
28
28
|
this.inputType = "ArrayBuffer";
|
|
29
29
|
this.outputType = "List<File>";
|
|
@@ -48,26 +48,44 @@ class SplitColourChannels extends Operation {
|
|
|
48
48
|
const split = parsedImage
|
|
49
49
|
.clone()
|
|
50
50
|
.color([
|
|
51
|
-
{apply: "blue", params: [-255]},
|
|
52
|
-
{apply: "green", params: [-255]}
|
|
51
|
+
{ apply: "blue", params: [-255] },
|
|
52
|
+
{ apply: "green", params: [-255] },
|
|
53
53
|
])
|
|
54
|
-
.
|
|
55
|
-
resolve(
|
|
54
|
+
.getBuffer(JimpMime.png);
|
|
55
|
+
resolve(
|
|
56
|
+
new File(
|
|
57
|
+
[new Uint8Array((await split).values())],
|
|
58
|
+
"red.png",
|
|
59
|
+
{ type: "image/png" },
|
|
60
|
+
),
|
|
61
|
+
);
|
|
56
62
|
} catch (err) {
|
|
57
|
-
reject(
|
|
63
|
+
reject(
|
|
64
|
+
new OperationError(`Could not split red channel: ${err}`),
|
|
65
|
+
);
|
|
58
66
|
}
|
|
59
67
|
});
|
|
60
68
|
|
|
61
69
|
const green = new Promise(async (resolve, reject) => {
|
|
62
70
|
try {
|
|
63
|
-
const split = parsedImage
|
|
71
|
+
const split = parsedImage
|
|
72
|
+
.clone()
|
|
64
73
|
.color([
|
|
65
|
-
{apply: "red", params: [-255]},
|
|
66
|
-
{apply: "blue", params: [-255]},
|
|
67
|
-
])
|
|
68
|
-
|
|
74
|
+
{ apply: "red", params: [-255] },
|
|
75
|
+
{ apply: "blue", params: [-255] },
|
|
76
|
+
])
|
|
77
|
+
.getBuffer(JimpMime.png);
|
|
78
|
+
resolve(
|
|
79
|
+
new File(
|
|
80
|
+
[new Uint8Array((await split).values())],
|
|
81
|
+
"green.png",
|
|
82
|
+
{ type: "image/png" },
|
|
83
|
+
),
|
|
84
|
+
);
|
|
69
85
|
} catch (err) {
|
|
70
|
-
reject(
|
|
86
|
+
reject(
|
|
87
|
+
new OperationError(`Could not split green channel: ${err}`),
|
|
88
|
+
);
|
|
71
89
|
}
|
|
72
90
|
});
|
|
73
91
|
|
|
@@ -75,12 +93,21 @@ class SplitColourChannels extends Operation {
|
|
|
75
93
|
try {
|
|
76
94
|
const split = parsedImage
|
|
77
95
|
.color([
|
|
78
|
-
{apply: "red", params: [-255]},
|
|
79
|
-
{apply: "green", params: [-255]},
|
|
80
|
-
])
|
|
81
|
-
|
|
96
|
+
{ apply: "red", params: [-255] },
|
|
97
|
+
{ apply: "green", params: [-255] },
|
|
98
|
+
])
|
|
99
|
+
.getBuffer(JimpMime.png);
|
|
100
|
+
resolve(
|
|
101
|
+
new File(
|
|
102
|
+
[new Uint8Array((await split).values())],
|
|
103
|
+
"blue.png",
|
|
104
|
+
{ type: "image/png" },
|
|
105
|
+
),
|
|
106
|
+
);
|
|
82
107
|
} catch (err) {
|
|
83
|
-
reject(
|
|
108
|
+
reject(
|
|
109
|
+
new OperationError(`Could not split blue channel: ${err}`),
|
|
110
|
+
);
|
|
84
111
|
}
|
|
85
112
|
});
|
|
86
113
|
|
|
@@ -96,7 +123,6 @@ class SplitColourChannels extends Operation {
|
|
|
96
123
|
async present(files) {
|
|
97
124
|
return await Utils.displayFilesAsHTML(files);
|
|
98
125
|
}
|
|
99
|
-
|
|
100
126
|
}
|
|
101
127
|
|
|
102
128
|
export default SplitColourChannels;
|