WIP: Card compositing
This commit is contained in:
37
commands/view.js
Normal file
37
commands/view.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
const { SlashCommandBuilder, AttachmentBuilder } = require("discord.js");
|
||||||
|
const { Card, User, Character } = require("../models");
|
||||||
|
const Rendering = require("../util/rendering");
|
||||||
|
|
||||||
|
//fetch all cards owned by the user and list them
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("view")
|
||||||
|
.setDescription("View a specific card")
|
||||||
|
.addStringOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("card")
|
||||||
|
.setDescription("Card identifier")
|
||||||
|
.setRequired(false)
|
||||||
|
),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply();
|
||||||
|
const cardId = interaction.options.getString('card');
|
||||||
|
const card = await Card.findOne({
|
||||||
|
where: {
|
||||||
|
identifier: cardId
|
||||||
|
},
|
||||||
|
include: [Character]
|
||||||
|
});
|
||||||
|
if (!card) {
|
||||||
|
interaction.reply({
|
||||||
|
content: "Card not found",
|
||||||
|
ephemeral: true
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cardImage = await Rendering.renderCard(card);
|
||||||
|
const message = await interaction.editReply({ content: '', files: [new AttachmentBuilder(cardImage, { name: 'card.gif' })], fetchReply: true });
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,15 @@
|
|||||||
const sharp = require('sharp');
|
const sharp = require('sharp');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const fs = require('fs');
|
||||||
const { Character } = require('../models');
|
const { Character } = require('../models');
|
||||||
|
|
||||||
|
//TODO: Handle missing images
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: "Rendering",
|
name: "Rendering",
|
||||||
renderCardStack: async function(cards) {
|
renderCardStack: async function(cards) {
|
||||||
for (let card of cards) {
|
for (let card of cards) {
|
||||||
const character = await Character.findOne({
|
console.log(`Iterating card ${card.id}`);
|
||||||
where: {
|
card['render'] = await this.renderCard(card);
|
||||||
id: card.characterId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
card.imageIdentifier = card.userId ? character.imageIdentifier : 'card_cover.png';
|
|
||||||
|
|
||||||
const cardImage = await sharp(`./assets/cards/${card.imageIdentifier}`);
|
|
||||||
if (card.userId === 1) {
|
|
||||||
cardImage.grayscale()
|
|
||||||
.modulate({
|
|
||||||
brightness: 0.5
|
|
||||||
});
|
|
||||||
}
|
|
||||||
card.preProcessed = await cardImage.png().toBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = await sharp({
|
const image = await sharp({
|
||||||
@@ -27,20 +17,59 @@ module.exports = {
|
|||||||
width: 900,
|
width: 900,
|
||||||
height: 500,
|
height: 500,
|
||||||
channels: 4,
|
channels: 4,
|
||||||
background: { r: 255, g: 0, b: 0, alpha: 0.5 }
|
background: { r: 255, g: 0, b: 0, alpha: 0.5 },
|
||||||
|
animated: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.composite([
|
.composite([
|
||||||
{ input: cards[0].preProcessed, gravity: 'northwest' },
|
{ input: cards[0].render, gravity: 'northwest' },
|
||||||
{ input: cards[1].preProcessed, gravity: 'centre' },
|
{ input: cards[1].render, gravity: 'centre' },
|
||||||
{ input: cards[2].preProcessed, gravity: 'northeast' },
|
{ input: cards[2].render, gravity: 'northeast' },
|
||||||
{ input: './assets/overlays/rainbow_overlay.png', gravity: 'northwest' },
|
]);
|
||||||
{ input: './assets/overlays/rainbow_overlay.png', gravity: 'centre' },
|
|
||||||
{ input: './assets/overlays/rainbow_overlay.png', gravity: 'northeast' },
|
|
||||||
])
|
|
||||||
.png()
|
|
||||||
.toBuffer();
|
|
||||||
|
|
||||||
return image;
|
|
||||||
|
await image.gif({effort: 1}).toFile(`./assets/image_cache/test.gif`);
|
||||||
|
|
||||||
|
return `./assets/image_cache/test.gif`;
|
||||||
|
|
||||||
|
},
|
||||||
|
renderCard: async function(card) {
|
||||||
|
const character = await Character.findOne({
|
||||||
|
where: {
|
||||||
|
id: card.characterId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
card.imageIdentifier = card.userId ? character.imageIdentifier : 'card_cover.png';
|
||||||
|
|
||||||
|
let hash = crypto.createHash('md5').update(card.imageIdentifier).digest('hex');
|
||||||
|
//TODO: Add switch to turn off or bypass caching
|
||||||
|
if (fs.existsSync(`./assets/image_cache/${hash}.gif`)) {
|
||||||
|
return fs.createReadStream(`./assets/image_cache/${hash}.gif`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Rendering card ${hash}`);
|
||||||
|
|
||||||
|
let border = await sharp(`./assets/border.svg`).toBuffer();
|
||||||
|
//BUGBUG: Custom fonts not loading
|
||||||
|
let label = Buffer.from(`
|
||||||
|
<svg width="300" height="500">
|
||||||
|
<text x="50%" y="95%" text-anchor="middle" style="font-size:28px;">${character.name}</text>
|
||||||
|
</svg>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const cardImage = await sharp(`./assets/cards/${card.imageIdentifier}`, { animated: true, pages: -1 });
|
||||||
|
await cardImage.resize(300, 500);
|
||||||
|
await cardImage.composite([
|
||||||
|
{input: border, top:0, left: 0, tile: true},
|
||||||
|
{input: label, top:0, left: 0, tile: true}]);
|
||||||
|
if (card.userId === 1) {
|
||||||
|
cardImage.grayscale()
|
||||||
|
.modulate({
|
||||||
|
brightness: 0.5
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await cardImage.gif({effort: 1}).toFile(`./assets/image_cache/${hash}.gif`);
|
||||||
|
|
||||||
|
return `./assets/image_cache/${hash}.gif`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user