Skip to content

Commit

Permalink
Merge branch 'generate_image' of https://github.com/pointhi/CyberChef
Browse files Browse the repository at this point in the history
…into pointhi-generate_image
  • Loading branch information
n1474335 committed Mar 13, 2020
2 parents 707818a + ef61735 commit 032c7f5
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@
"name": "Multimedia",
"ops": [
"Render Image",
"Generate Image",
"Play Media",
"Optical Character Recognition",
"Remove EXIF",
Expand Down
185 changes: 185 additions & 0 deletions src/core/operations/GenerateImage.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/**
* @author pointhi [thomas.pointhuber@gmx.at]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import {isImage} from "../lib/FileType";
import {toBase64} from "../lib/Base64";
import jimp from "jimp";
import {isWorkerEnvironment} from "../Utils";

/**
* Generate Image operation
*/
class GenerateImage extends Operation {

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

this.name = "Generate Image";
this.module = "Image";
this.description = "Generate an Image using the input as pixel values.";
this.infoURL = "";
this.inputType = "byteArray";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
"name": "Mode",
"type": "option",
"value": ["Greyscale", "RG", "RGB", "RGBA", "Bits"]
},
{
"name": "Pixel Scale Factor",
"type": "number",
"value": 8,
},
{
"name": "Pixels per Row",
"type": "number",
"value": 64,
}
];
}

/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
async run(input, args) {
const mode = args[0];
const scale = args[1];
const width = args[2];

if (scale <= 0) {
throw new OperationError("Pixel Scale Factor needs to be > 0");
}

if (width <= 0) {
throw new OperationError("Pixels per Row needs to be > 0");
}

const bytePerPixelMap = {
"Greyscale": 1,
"RG": 2,
"RGB": 3,
"RGBA": 4,
"Bits": 1/8,
};

const bytesPerPixel = bytePerPixelMap[mode];

if (bytesPerPixel > 0 && input.length % bytesPerPixel !== 0) {
throw new OperationError(`Number of bytes is not a divisor of ${bytesPerPixel}`);
}

const height = Math.ceil(input.length / bytesPerPixel / width);
const image = await new jimp(width, height, (err, image) => {});

if (isWorkerEnvironment())
self.sendStatusMessage("Generate image from data...");

if (mode === "Bits") {
let index = 0;
for (let j = 0; j < input.length; j++) {
const curByte = Utils.bin(input[j]);
for (let k = 0; k < 8; k++, index++) {
const x = index % width;
const y = Math.floor(index / width);

const value = curByte[k] === "0" ? 0xFF : 0x00;
const pixel = jimp.rgbaToInt(value, value, value, 0xFF);
image.setPixelColor(pixel, x, y);
}
}
} else {
let i = 0;
while (i < input.length) {
const index = i / bytesPerPixel;
const x = index % width;
const y = Math.floor(index / width);

let red = 0x00;
let green = 0x00;
let blue = 0x00;
let alpha = 0xFF;

switch (mode) {
case "Greyscale":
red = green = blue = input[i++];
break;

case "RG":
red = input[i++];
green = input[i++];
break;

case "RGB":
red = input[i++];
green = input[i++];
blue = input[i++];
break;

case "RGBA":
red = input[i++];
green = input[i++];
blue = input[i++];
alpha = input[i++];
break;

default:
throw new OperationError(`Unsupported Mode: (${mode})`);
}

try {
const pixel = jimp.rgbaToInt(red, green, blue, alpha);
image.setPixelColor(pixel, x, y);
} catch (err) {
throw new OperationError(`Error while generating image from pixel values. (${err})`);
}
}
}

if (scale !== 1) {
if (isWorkerEnvironment())
self.sendStatusMessage("Scale image...");

image.scaleToFit(width*scale, height*scale, jimp.RESIZE_NEAREST_NEIGHBOR);
}

try {
const imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error generating image. (${err})`);
}
}

/**
* Displays the generated image using HTML for web apps
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);

const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}

return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}

}

export default GenerateImage;

0 comments on commit 032c7f5

Please sign in to comment.