diff --git a/.gitignore b/.gitignore index 2047fa2f..8b14c2d9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ report*.json config.json # In-Development Commands +assets/json/lorem-ipsum.json +commands/random-res/lorem-ipsum.js diff --git a/README.md b/README.md index b3e25013..6cca8ede 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ in the appropriate channel's topic to use it. ## Commands -Total: 428 +Total: 429 ### Utility: @@ -525,6 +525,7 @@ Total: 428 * **dexter:** Draws an image or a user's avatar over the screen of Dexter from Pokémon. * **distort:** Draws an image or a user's avatar but distorted. * **fire:** Draws a fiery border over an image or a user's avatar. +* **fish-eye:** Draws an image or a user's avatar but with a fish-eye lens. * **frame:** Draws a frame around an image or a user's avatar. * **ghost:** Draws an image or a user's avatar but with a ghost-like transparency. * **glass-shatter:** Draws an image or a user's avatar with a glass shatter in front of it. @@ -947,6 +948,8 @@ here. * word-chain ([Moby Word Lists](http://www.gutenberg.org/ebooks/3201)) - [Gravatar](https://en.gravatar.com/) * gravatar ([API](https://en.gravatar.com/site/implement/)) +- [Hackyon](http://www.hackyon.com/playground/fisheye/) + * fish-eye (Fish-Eye Code) - [Hasbro](https://shop.hasbro.com/en-us) * bro-hoof ([Original "My Little Pony: Friendship is Magic" Show](https://mylittlepony.hasbro.com/en-us)) * brony-speak ([Original "My Little Pony: Friendship is Magic" Show](https://mylittlepony.hasbro.com/en-us)) diff --git a/commands/edit-image/fish-eye.js b/commands/edit-image/fish-eye.js new file mode 100644 index 00000000..be8b8939 --- /dev/null +++ b/commands/edit-image/fish-eye.js @@ -0,0 +1,59 @@ +const Command = require('../../structures/Command'); +const { createCanvas, loadImage } = require('canvas'); +const request = require('node-superfetch'); +const { fishEye } = require('../../util/Canvas'); + +module.exports = class FishEyeCommand extends Command { + constructor(client) { + super(client, { + name: 'fish-eye', + aliases: ['bulge'], + group: 'edit-image', + memberName: 'fish-eye', + description: 'Draws an image or a user\'s avatar but with a fish-eye lens.', + throttling: { + usages: 1, + duration: 10 + }, + clientPermissions: ['ATTACH_FILES'], + credit: [ + { + name: 'Hackyon', + url: 'http://www.hackyon.com/playground/fisheye/', + reason: 'Fish-Eye Code' + } + ], + args: [ + { + key: 'level', + prompt: 'What level of distortion would you like to use?', + type: 'integer', + min: 1, + max: 100 + }, + { + key: 'image', + prompt: 'What image would you like to edit?', + type: 'image', + default: msg => msg.author.displayAvatarURL({ format: 'png', size: 512 }) + } + ] + }); + } + + async run(msg, { level, image }) { + try { + const { body } = await request.get(image); + const data = await loadImage(body); + const canvas = createCanvas(data.width, data.height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(data, 0, 0); + fishEye(ctx, level, 0, 0, data.width, data.height); + const attachment = canvas.toBuffer(); + if (Buffer.byteLength(attachment) > 8e+6) return msg.reply('Resulting image was above 8 MB.'); + return msg.say({ files: [{ attachment, name: 'fish-eye.png' }] }); + } catch (err) { + return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + } + } +}; diff --git a/package.json b/package.json index faf40750..f8cb2d01 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "114.17.2", + "version": "114.18.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/util/Canvas.js b/util/Canvas.js index f561f310..63359dc0 100644 --- a/util/Canvas.js +++ b/util/Canvas.js @@ -79,6 +79,27 @@ module.exports = class CanvasUtil { return ctx; } + static fishEye(ctx, level, x, y, width, height) { + const frame = ctx.getImageData(x, y, width, height); + const source = new Uint8Array(frame.data); + for (let i = 0; i < frame.data.length; i += 4) { + const sx = (i / 4) % frame.width; + const sy = Math.floor(i / 4 / frame.width); + const dx = Math.floor(frame.width / 2) - sx; + const dy = Math.floor(frame.height / 2) - sy; + const dist = Math.sqrt((dx * dx) + (dy * dy)); + const x2 = Math.round((frame.width / 2) - (dx * Math.sin(dist / (level * Math.PI) / 2))); + const y2 = Math.round((frame.height / 2) - (dy * Math.sin(dist / (level * Math.PI) / 2))); + const i2 = (y2 * frame.width) + (x2 * 4); + frame.data[i] = source[i2]; + frame.data[i + 1] = source[i2 + 1]; + frame.data[i + 2] = source[i2 + 2]; + frame.data[i + 3] = source[i2 + 3]; + } + ctx.putImageData(frame, x, y); + return ctx; + } + static hasAlpha(image) { const canvas = createCanvas(image.width, image.height); const ctx = canvas.getContext('2d');