diff --git a/src/core/Dish.mjs b/src/core/Dish.mjs index 9c878cf6..64bdf3fb 100755 --- a/src/core/Dish.mjs +++ b/src/core/Dish.mjs @@ -177,7 +177,7 @@ class Dish { this.type = type; if (!this.valid()) { - const sample = Utils.truncate(JSON.stringify(this.value), 13); + const sample = Utils.truncate(JSON.stringify(this.value), 25); throw new DishError(`Data is not a valid ${Dish.enumLookup(type)}: ${sample}`); } } diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 351d1ed0..73ac6c2d 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -369,7 +369,11 @@ "Scan for Embedded Files", "Extract Files", "Remove EXIF", - "Extract EXIF" + "Extract EXIF", + "Extract RGBA", + "View Bit Plane", + "Randomize Colour Palette", + "Extract LSB" ] }, { @@ -380,10 +384,6 @@ "Remove EXIF", "Extract EXIF", "Split Colour Channels", - "Extract RGBA", - "View Bit Plane", - "Randomize Colour Palette", - "Extract LSB", "Rotate Image", "Resize Image", "Blur Image", diff --git a/src/core/lib/Base64.mjs b/src/core/lib/Base64.mjs index 0d8788f9..7730ae07 100644 --- a/src/core/lib/Base64.mjs +++ b/src/core/lib/Base64.mjs @@ -12,7 +12,7 @@ import Utils from "../Utils.mjs"; /** * Base64's the input byte array using the given alphabet, returning a string. * - * @param {byteArray|Uint8Array|string} data + * @param {byteArray|Uint8Array|ArrayBuffer|string} data * @param {string} [alphabet="A-Za-z0-9+/="] * @returns {string} * @@ -25,6 +25,9 @@ import Utils from "../Utils.mjs"; */ export function toBase64(data, alphabet="A-Za-z0-9+/=") { if (!data) return ""; + if (data instanceof ArrayBuffer) { + data = new Uint8Array(data); + } if (typeof data == "string") { data = Utils.strToByteArray(data); } diff --git a/src/core/lib/Delim.mjs b/src/core/lib/Delim.mjs index eb390e64..0a7193c6 100644 --- a/src/core/lib/Delim.mjs +++ b/src/core/lib/Delim.mjs @@ -72,9 +72,9 @@ export const JOIN_DELIM_OPTIONS = [ {name: "Nothing (join chars)", value: ""} ]; -/* - RGBA list delimiters. -*/ +/** + * RGBA list delimiters. + */ export const RGBA_DELIM_OPTIONS = [ {name: "Comma", value: ","}, {name: "Space", value: " "}, diff --git a/src/core/lib/FileType.mjs b/src/core/lib/FileType.mjs index 9a3de442..dc6cc973 100644 --- a/src/core/lib/FileType.mjs +++ b/src/core/lib/FileType.mjs @@ -75,7 +75,7 @@ function bytesMatch(sig, buf, offset=0) { * Given a buffer, detects magic byte sequences at specific positions and returns the * extension and mime type. * - * @param {Uint8Array} buf + * @param {Uint8Array|ArrayBuffer} buf * @param {string[]} [categories=All] - Which categories of file to look for * @returns {Object[]} types * @returns {string} type.name - Name of file type @@ -84,6 +84,10 @@ function bytesMatch(sig, buf, offset=0) { * @returns {string} [type.desc] - Description */ export function detectFileType(buf, categories=Object.keys(FILE_SIGNATURES)) { + if (buf instanceof ArrayBuffer) { + buf = new Uint8Array(buf); + } + if (!(buf && buf.length > 1)) { return []; } @@ -203,7 +207,7 @@ function locatePotentialSig(buf, sig, offset) { * Detects whether the given buffer is a file of the type specified. * * @param {string|RegExp} type - * @param {Uint8Array} buf + * @param {Uint8Array|ArrayBuffer} buf * @returns {string|false} The mime type or false if the type does not match */ export function isType(type, buf) { @@ -230,7 +234,7 @@ export function isType(type, buf) { /** * Detects whether the given buffer contains an image file. * - * @param {Uint8Array} buf + * @param {Uint8Array|ArrayBuffer} buf * @returns {string|false} The mime type or false if the type does not match */ export function isImage(buf) { diff --git a/src/core/operations/AddTextToImage.mjs b/src/core/operations/AddTextToImage.mjs index 3746c18d..084c92ec 100644 --- a/src/core/operations/AddTextToImage.mjs +++ b/src/core/operations/AddTextToImage.mjs @@ -121,7 +121,7 @@ class AddTextToImage extends Operation { let xPos = args[3], yPos = args[4]; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/BlurImage.mjs b/src/core/operations/BlurImage.mjs index c6f54421..ec254433 100644 --- a/src/core/operations/BlurImage.mjs +++ b/src/core/operations/BlurImage.mjs @@ -53,7 +53,7 @@ class BlurImage extends Operation { async run(input, args) { const [blurAmount, blurType] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ContainImage.mjs b/src/core/operations/ContainImage.mjs index 7da50ee6..09717a28 100644 --- a/src/core/operations/ContainImage.mjs +++ b/src/core/operations/ContainImage.mjs @@ -107,7 +107,7 @@ class ContainImage extends Operation { "Bottom": jimp.VERTICAL_ALIGN_BOTTOM }; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ConvertImageFormat.mjs b/src/core/operations/ConvertImageFormat.mjs index b0cc4982..8d6dce73 100644 --- a/src/core/operations/ConvertImageFormat.mjs +++ b/src/core/operations/ConvertImageFormat.mjs @@ -93,7 +93,7 @@ class ConvertImageFormat extends Operation { const mime = formatMap[format]; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file format."); } let image; diff --git a/src/core/operations/CoverImage.mjs b/src/core/operations/CoverImage.mjs index f216b1f9..07838ecf 100644 --- a/src/core/operations/CoverImage.mjs +++ b/src/core/operations/CoverImage.mjs @@ -102,7 +102,7 @@ class CoverImage extends Operation { "Bottom": jimp.VERTICAL_ALIGN_BOTTOM }; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/CropImage.mjs b/src/core/operations/CropImage.mjs index e74a1617..8b480080 100644 --- a/src/core/operations/CropImage.mjs +++ b/src/core/operations/CropImage.mjs @@ -93,7 +93,7 @@ class CropImage extends Operation { */ async run(input, args) { const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/DitherImage.mjs b/src/core/operations/DitherImage.mjs index 469711e7..6aef72dc 100644 --- a/src/core/operations/DitherImage.mjs +++ b/src/core/operations/DitherImage.mjs @@ -38,7 +38,7 @@ class DitherImage extends Operation { * @returns {byteArray} */ async run(input, args) { - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ExtractLSB.mjs b/src/core/operations/ExtractLSB.mjs index cd6128d3..12f990da 100644 --- a/src/core/operations/ExtractLSB.mjs +++ b/src/core/operations/ExtractLSB.mjs @@ -6,8 +6,9 @@ import Operation from "../Operation.mjs"; import OperationError from "../errors/OperationError.mjs"; -import Utils from "../Utils"; -import { isImage } from "../lib/FileType"; +import Utils from "../Utils.mjs"; +import { fromBinary } from "../lib/Binary.mjs"; +import { isImage } from "../lib/FileType.mjs"; import jimp from "jimp"; /** @@ -24,8 +25,8 @@ class ExtractLSB extends Operation { this.name = "Extract LSB"; this.module = "Image"; this.description = "Extracts the Least Significant Bit data from each pixel in an image. This is a common way to hide data in Steganography."; - this.infoURL = "https://en.wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography"; - this.inputType = "byteArray"; + this.infoURL = "https://wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography"; + this.inputType = "ArrayBuffer"; this.outputType = "byteArray"; this.args = [ { @@ -62,9 +63,9 @@ class ExtractLSB extends Operation { } /** - * @param {File} input + * @param {ArrayBuffer} input * @param {Object[]} args - * @returns {File} + * @returns {byteArray} */ async run(input, args) { if (!isImage(input)) throw new OperationError("Please enter a valid image file."); @@ -72,7 +73,7 @@ class ExtractLSB extends Operation { const bit = 7 - args.pop(), pixelOrder = args.pop(), colours = args.filter(option => option !== "").map(option => COLOUR_OPTIONS.indexOf(option)), - parsedImage = await jimp.read(Buffer.from(input)), + parsedImage = await jimp.read(input), width = parsedImage.bitmap.width, height = parsedImage.bitmap.height, rgba = parsedImage.bitmap.data; @@ -103,8 +104,7 @@ class ExtractLSB extends Operation { } } - return Utils.convertToByteArray(combinedBinary, "binary"); - + return fromBinary(combinedBinary); } } diff --git a/src/core/operations/ExtractRGBA.mjs b/src/core/operations/ExtractRGBA.mjs index a215f58b..7d2fc274 100644 --- a/src/core/operations/ExtractRGBA.mjs +++ b/src/core/operations/ExtractRGBA.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation.mjs"; import OperationError from "../errors/OperationError.mjs"; -import { isImage } from "../lib/FileType"; +import { isImage } from "../lib/FileType.mjs"; import jimp from "jimp"; import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs"; @@ -25,8 +25,8 @@ class ExtractRGBA extends Operation { this.name = "Extract RGBA"; this.module = "Image"; this.description = "Extracts each pixel's RGBA value in an image. These are sometimes used in Steganography to hide text or data."; - this.infoURL = "https://en.wikipedia.org/wiki/RGBA_color_space"; - this.inputType = "byteArray"; + this.infoURL = "https://wikipedia.org/wiki/RGBA_color_space"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -43,7 +43,7 @@ class ExtractRGBA extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ @@ -52,7 +52,7 @@ class ExtractRGBA extends Operation { const delimiter = args[0], includeAlpha = args[1], - parsedImage = await jimp.read(Buffer.from(input)); + parsedImage = await jimp.read(input); let bitmap = parsedImage.bitmap.data; bitmap = includeAlpha ? bitmap : bitmap.filter((val, idx) => idx % 4 !== 3); diff --git a/src/core/operations/FlipImage.mjs b/src/core/operations/FlipImage.mjs index a6a262fc..30be5a4e 100644 --- a/src/core/operations/FlipImage.mjs +++ b/src/core/operations/FlipImage.mjs @@ -45,7 +45,7 @@ class FlipImage extends Operation { */ async run(input, args) { const [flipAxis] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid input file type."); } diff --git a/src/core/operations/ImageBrightnessContrast.mjs b/src/core/operations/ImageBrightnessContrast.mjs index 81c001dc..3845bebb 100644 --- a/src/core/operations/ImageBrightnessContrast.mjs +++ b/src/core/operations/ImageBrightnessContrast.mjs @@ -54,7 +54,7 @@ class ImageBrightnessContrast extends Operation { */ async run(input, args) { const [brightness, contrast] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ImageFilter.mjs b/src/core/operations/ImageFilter.mjs index 95c89197..137dd0fc 100644 --- a/src/core/operations/ImageFilter.mjs +++ b/src/core/operations/ImageFilter.mjs @@ -48,7 +48,7 @@ class ImageFilter extends Operation { */ async run(input, args) { const [filterType] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ImageHueSaturationLightness.mjs b/src/core/operations/ImageHueSaturationLightness.mjs index 7f5cd8cb..b0bad682 100644 --- a/src/core/operations/ImageHueSaturationLightness.mjs +++ b/src/core/operations/ImageHueSaturationLightness.mjs @@ -62,7 +62,7 @@ class ImageHueSaturationLightness extends Operation { async run(input, args) { const [hue, saturation, lightness] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ImageOpacity.mjs b/src/core/operations/ImageOpacity.mjs index cc717d58..622ee96c 100644 --- a/src/core/operations/ImageOpacity.mjs +++ b/src/core/operations/ImageOpacity.mjs @@ -47,7 +47,7 @@ class ImageOpacity extends Operation { */ async run(input, args) { const [opacity] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/InvertImage.mjs b/src/core/operations/InvertImage.mjs index 22830701..6b62dea7 100644 --- a/src/core/operations/InvertImage.mjs +++ b/src/core/operations/InvertImage.mjs @@ -38,7 +38,7 @@ class InvertImage extends Operation { * @returns {byteArray} */ async run(input, args) { - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid input file format."); } diff --git a/src/core/operations/NormaliseImage.mjs b/src/core/operations/NormaliseImage.mjs index c40bbc33..08de9ed5 100644 --- a/src/core/operations/NormaliseImage.mjs +++ b/src/core/operations/NormaliseImage.mjs @@ -37,7 +37,7 @@ class NormaliseImage extends Operation { * @returns {byteArray} */ async run(input, args) { - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ParseQRCode.mjs b/src/core/operations/ParseQRCode.mjs index 13849e71..6f34a6d0 100644 --- a/src/core/operations/ParseQRCode.mjs +++ b/src/core/operations/ParseQRCode.mjs @@ -51,7 +51,7 @@ class ParseQRCode extends Operation { async run(input, args) { const [normalise] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } return await parseQrCode(input, normalise); diff --git a/src/core/operations/RandomizeColourPalette.mjs b/src/core/operations/RandomizeColourPalette.mjs index ded8fd49..cf969718 100644 --- a/src/core/operations/RandomizeColourPalette.mjs +++ b/src/core/operations/RandomizeColourPalette.mjs @@ -6,12 +6,12 @@ import Operation from "../Operation.mjs"; import OperationError from "../errors/OperationError.mjs"; -import Utils from "../Utils"; -import PseudoRandomNumberGenerator from "./PseudoRandomNumberGenerator.mjs"; -import { isImage } from "../lib/FileType"; +import Utils from "../Utils.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { runHash } from "../lib/Hash.mjs"; -import { toBase64 } from "../lib/Base64"; +import { toBase64 } from "../lib/Base64.mjs"; import jimp from "jimp"; +import { toHex } from "../lib/Hex.mjs"; /** * Randomize Colour Palette operation @@ -26,10 +26,10 @@ class RandomizeColourPalette extends Operation { this.name = "Randomize Colour Palette"; this.module = "Image"; - this.description = "Randomize's 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."; - this.infoURL = "https://en.wikipedia.org/wiki/Indexed_color"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.description = "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."; + this.infoURL = "https://wikipedia.org/wiki/Indexed_color"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -41,15 +41,15 @@ class RandomizeColourPalette extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args - * @returns {byteArray} + * @returns {ArrayBuffer} */ async run(input, args) { if (!isImage(input)) throw new OperationError("Please enter a valid image file."); - const seed = args[0] || (new PseudoRandomNumberGenerator()).run("", [5, "Hex"]), - parsedImage = await jimp.read(Buffer.from(input)), + const seed = args[0] || (Math.random().toString().substr(2)), + parsedImage = await jimp.read(input), width = parsedImage.bitmap.width, height = parsedImage.bitmap.height; @@ -64,16 +64,16 @@ class RandomizeColourPalette extends Operation { const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO); - return Array.from(imageBuffer); + return new Uint8Array(imageBuffer).buffer; } /** * Displays the extracted data as an image for web apps. - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; const type = isImage(data); return ``; diff --git a/src/core/operations/ResizeImage.mjs b/src/core/operations/ResizeImage.mjs index 57a8cd9f..b2ed3bbf 100644 --- a/src/core/operations/ResizeImage.mjs +++ b/src/core/operations/ResizeImage.mjs @@ -87,7 +87,7 @@ class ResizeImage extends Operation { "Bezier": jimp.RESIZE_BEZIER }; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/RotateImage.mjs b/src/core/operations/RotateImage.mjs index b2075706..a4659b12 100644 --- a/src/core/operations/RotateImage.mjs +++ b/src/core/operations/RotateImage.mjs @@ -46,7 +46,7 @@ class RotateImage extends Operation { async run(input, args) { const [degrees] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/SharpenImage.mjs b/src/core/operations/SharpenImage.mjs index 338679c0..e7b0eaa2 100644 --- a/src/core/operations/SharpenImage.mjs +++ b/src/core/operations/SharpenImage.mjs @@ -62,7 +62,7 @@ class SharpenImage extends Operation { async run(input, args) { const [radius, amount, threshold] = args; - if (!isImage(new Uint8Array(input))) { + if (!isImage(input)) { throw new OperationError("Invalid file type."); } diff --git a/src/core/operations/ToBase64.mjs b/src/core/operations/ToBase64.mjs index 93dd4f81..53516a9f 100644 --- a/src/core/operations/ToBase64.mjs +++ b/src/core/operations/ToBase64.mjs @@ -40,7 +40,7 @@ class ToBase64 extends Operation { */ run(input, args) { const alphabet = args[0]; - return toBase64(new Uint8Array(input), alphabet); + return toBase64(input, alphabet); } /** diff --git a/src/core/operations/ViewBitPlane.mjs b/src/core/operations/ViewBitPlane.mjs index 864b196c..f98db2f3 100644 --- a/src/core/operations/ViewBitPlane.mjs +++ b/src/core/operations/ViewBitPlane.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import { isImage } from "../lib/FileType"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; import jimp from "jimp"; /** @@ -24,10 +24,10 @@ class ViewBitPlane extends Operation { this.name = "View Bit Plane"; this.module = "Image"; - this.description = "Extracts and displays a bit plane of any given image. These show only a single bit from each pixel, and so are often used to hide messages in Steganography."; + this.description = "Extracts and displays a bit plane of any given image. These show only a single bit from each pixel, and can be used to hide messages in Steganography."; this.infoURL = "https://wikipedia.org/wiki/Bit_plane"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -44,15 +44,15 @@ class ViewBitPlane extends Operation { } /** - * @param {File} input + * @param {ArrayBuffer} input * @param {Object[]} args - * @returns {File} + * @returns {ArrayBuffer} */ async run(input, args) { if (!isImage(input)) throw new OperationError("Please enter a valid image file."); const [colour, bit] = args, - parsedImage = await jimp.read(Buffer.from(input)), + parsedImage = await jimp.read(input), width = parsedImage.bitmap.width, height = parsedImage.bitmap.height, colourIndex = COLOUR_OPTIONS.indexOf(colour), @@ -62,7 +62,6 @@ class ViewBitPlane extends Operation { throw new OperationError("Error: Bit argument must be between 0 and 7"); } - let pixel, bin, newPixelValue; parsedImage.scan(0, 0, width, height, function(x, y, idx) { @@ -81,12 +80,12 @@ class ViewBitPlane extends Operation { const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO); - return Array.from(imageBuffer); + return new Uint8Array(imageBuffer).buffer; } /** * Displays the extracted data as an image for web apps. - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) {