Natives Rework (Thank you Essem)

Co-authored-by: Essem <TheEssem@users.noreply.github.com>
This commit is contained in:
murm 2023-03-19 04:40:32 -04:00
parent b424b2f813
commit ff7f0a3110
88 changed files with 3358 additions and 3104 deletions

42
app.js
View file

@ -75,27 +75,27 @@ async function* getFiles(dir) {
async function init() { async function init() {
await exec("git rev-parse HEAD").then(output => output.stdout.substring(0, 7), () => "unknown commit").then(o => process.env.GIT_REV = o); await exec("git rev-parse HEAD").then(output => output.stdout.substring(0, 7), () => "unknown commit").then(o => process.env.GIT_REV = o);
console.log(` console.log(`
,*\`$ z\`"v
F zBw\`% A ,W "W
,\` ,EBBBWp"%. ,-=~~==-,+* 4BBE T
M BBBBBBBB* ,w=####Wpw 4BBBBB# 1
F BBBBBBBMwBBBBBBBBBBBBB#wXBBBBBH E
F BBBBBBkBBBBBBBBBBBBBBBBBBBBE4BL k
# BFBBBBBBBBBBBBF" "RBBBW F
V ' 4BBBBBBBBBBM TBBL F
F BBBBBBBBBBF JBB L
F FBBBBBBBEB BBL 4
E [BB4BBBBEBL BBL 4
I #BBBBBBBEB 4BBH *w
A 4BBBBBBBBBEW, ,BBBB W [
.A ,k 4BBBBBBBBBBBEBW####BBBBBBM BF F
k <BBBw BBBBEBBBBBBBBBBBBBBBBBQ4BM #
5, REBBB4BBBBB#BBBBBBBBBBBBP5BFF ,F
*w \`*4BBW\`"FF#F##FFFF"\` , * +"
*+, " F'"'*^~~~^"^\` V+*^
\`"""
esmBot ${esmBotVersion} (${process.env.GIT_REV}) mrmBot-matrix ${esmBotVersion} (${process.env.GIT_REV})
`); `);
if (!types.classic && !types.application) { if (!types.classic && !types.application) {

View file

@ -79,14 +79,17 @@ class ImageCommand extends Command {
} }
try { try {
const { buffer, type } = await runImageJob(imageParams); const { buffer, type, width, height } = await runImageJob(imageParams);
if (type === "nogif" && this.constructor.requiresGIF) { if (type === "nogif" && this.constructor.requiresGIF) {
return "That isn't a GIF!"; return "That isn't a GIF!";
} }
this.success = true; this.success = true;
return { return {
contents: buffer, contents: buffer,
name: `${this.constructor.command}.${type}` name: `${this.constructor.command}.${type}`,
type,
width,
height
}; };
} catch (e) { } catch (e) {
if (e === "Request ended prematurely due to a closed connection") return "This image job couldn't be completed because the server it was running on went down. Try running your command again."; if (e === "Request ended prematurely due to a closed connection") return "This image job couldn't be completed because the server it was running on went down. Try running your command again.";

View file

@ -1,6 +1,10 @@
import ImageCommand from "../../classes/imageCommand.js"; import ImageCommand from "../../classes/imageCommand.js";
class ExplodeCommand extends ImageCommand { class ExplodeCommand extends ImageCommand {
params = {
implode: false
};
static category = "image-editing" static category = "image-editing"
static description = "Explodes an image"; static description = "Explodes an image";
static aliases = ["exp"]; static aliases = ["exp"];

View file

@ -2,6 +2,9 @@ import ImageCommand from "../../classes/imageCommand.js";
class FlipCommand extends ImageCommand { class FlipCommand extends ImageCommand {
static category = "image-editing" static category = "image-editing"
params = {
flop: false
};
static description = "Flips an image"; static description = "Flips an image";
static noImage = "You need to provide an image/GIF to flip!"; static noImage = "You need to provide an image/GIF to flip!";

View file

@ -99,11 +99,15 @@ export default async function (matrixClient, event, room, toStartOfTimeline) {
} else { } else {
const mxcUri = await matrixClient.uploadContent(result.contents); const mxcUri = await matrixClient.uploadContent(result.contents);
// TODO: make info object get width, height, and mime from natives so i dont need to read the buffer // TODO: make info object get width, height, and mime from natives so i dont need to read the buffer
const imgsize = sizeOf(result.contents) // const imgsize = sizeOf(result.contents)
let mime = imgsize.type; const mime = result.type;
if (mime == "jpg") { const imgsize = {
mime = "jpeg"; width: result.width,
height: result.height
} }
// if (mime === "jpg") {
// mime = "jpeg";
// }
await matrixClient.sendImageMessage(event.event.room_id, mxcUri.content_uri, {h: imgsize.height, w: imgsize.width, mimetype: `image/${mime}`, size: result.contents.length, thumbnail_info: {h: imgsize.height, w: imgsize.width, mimetype: `image/${mime}`, size: result.contents.length}}, result.name) await matrixClient.sendImageMessage(event.event.room_id, mxcUri.content_uri, {h: imgsize.height, w: imgsize.width, mimetype: `image/${mime}`, size: result.contents.length, thumbnail_info: {h: imgsize.height, w: imgsize.width, mimetype: `image/${mime}`, size: result.contents.length}}, result.name)
} }
} else { } else {

View file

@ -1,31 +1,36 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Blur(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Blur(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
bool sharp = GetArgument<bool>(Arguments, "sharp"); bool sharp = GetArgument<bool>(Arguments, "sharp");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
// TODO: find a better way to calculate the intensity for GIFs without // TODO: find a better way to calculate the intensity for GIFs without
// splitting frames // splitting frames
VImage out = VImage out =
sharp ? in.sharpen(VImage::option()->set("sigma", 3)) : in.gaussblur(15); sharp ? in.sharpen(VImage::option()->set("sigma", 3)) : in.gaussblur(15);
void *buf; void *buf;
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = out.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Blur(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Blur(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,51 +1,56 @@
#include <math.h> #include <math.h>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Bounce(string type, string *outType, char *BufferData, ArgumentMap Bounce(string type, string *outType, char *BufferData,
size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments,
size_t *DataSize) { size_t *DataSize) {
VOption *options = VImage::option(); VOption *options = VImage::option();
VImage in = VImage in =
VImage::new_from_buffer( VImage::new_from_buffer(
BufferData, BufferLength, "", BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1)->set("access", "sequential") type == "gif" ? options->set("n", -1)->set("access", "sequential")
: options) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 15; int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 15;
double mult = M_PI / nPages; double mult = M_PI / nPages;
int halfHeight = pageHeight / 2; int halfHeight = pageHeight / 2;
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
double height = halfHeight * ((abs(sin(i * mult)) * -1) + 1); double height = halfHeight * ((abs(sin(i * mult)) * -1) + 1);
VImage embedded = VImage embedded =
img_frame.embed(0, height, width, pageHeight + halfHeight); img_frame.embed(0, height, width, pageHeight + halfHeight);
img.push_back(embedded); img.push_back(embedded);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight + halfHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight + halfHeight);
if (type != "gif") { if (type != "gif") {
vector<int> delay(30, 50); vector<int> delay(30, 50);
final.set("delay", delay); final.set("delay", delay);
} }
void *buf; void *buf;
final.write_to_buffer(".gif", &buf, DataSize); final.write_to_buffer(".gif", &buf, DataSize);
*outType = "gif"; *outType = "gif";
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight + halfHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Bounce(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Bounce(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,77 +1,82 @@
#include <map> #include <map>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Caption(string type, string *outType, char *BufferData, ArgumentMap Caption(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string caption = GetArgument<string>(Arguments, "caption"); string caption = GetArgument<string>(Arguments, "caption");
string font = GetArgument<string>(Arguments, "font"); string font = GetArgument<string>(Arguments, "font");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int size = width / 10; int size = width / 10;
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int textWidth = width - ((width / 25) * 2); int textWidth = width - ((width / 25) * 2);
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " + string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " +
(font != "impact" ? "bold" : "normal") + " " + (font != "impact" ? "bold" : "normal") + " " +
to_string(size); to_string(size);
string captionText = "<span background=\"white\">" + caption + "</span>"; string captionText = "<span background=\"white\">" + caption + "</span>";
VImage text; VImage text;
auto findResult = fontPaths.find(font); auto findResult = fontPaths.find(font);
if (findResult != fontPaths.end()) { if (findResult != fontPaths.end()) {
text = VImage::text( text = VImage::text(
".", VImage::option()->set("fontfile", ".", VImage::option()->set("fontfile",
(basePath + findResult->second).c_str())); (basePath + findResult->second).c_str()));
} }
text = VImage::text( text = VImage::text(
captionText.c_str(), captionText.c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", textWidth)); ->set("width", textWidth));
VImage captionImage = VImage captionImage =
((text == (vector<double>){0, 0, 0, 0}).bandand()) ((text == (vector<double>){0, 0, 0, 0}).bandand())
.ifthenelse(255, text) .ifthenelse(255, text)
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, text.height() + size, .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, text.height() + size,
VImage::option()->set("extend", "white")); VImage::option()->set("extend", "white"));
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage frame = captionImage.join( VImage frame = captionImage.join(
img_frame, VIPS_DIRECTION_VERTICAL, img_frame, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("background", 0xffffff)->set("expand", true)); VImage::option()->set("background", 0xffffff)->set("expand", true));
img.push_back(frame); img.push_back(frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height()); final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height());
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Caption(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Caption(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,81 +1,86 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *CaptionTwo(string type, string *outType, char *BufferData, ArgumentMap CaptionTwo(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
bool top = GetArgument<bool>(Arguments, "top"); bool top = GetArgument<bool>(Arguments, "top");
string caption = GetArgument<string>(Arguments, "caption"); string caption = GetArgument<string>(Arguments, "caption");
string font = GetArgument<string>(Arguments, "font"); string font = GetArgument<string>(Arguments, "font");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int size = width / 13; int size = width / 13;
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int textWidth = width - ((width / 25) * 2); int textWidth = width - ((width / 25) * 2);
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + string font_string = (font == "roboto" ? "Roboto Condensed" : font) +
", Twemoji Color Emoji " + to_string(size); ", Twemoji Color Emoji " + to_string(size);
string captionText = "<span background=\"white\">" + caption + "</span>"; string captionText = "<span background=\"white\">" + caption + "</span>";
VImage text; VImage text;
auto findResult = fontPaths.find(font); auto findResult = fontPaths.find(font);
if (findResult != fontPaths.end()) { if (findResult != fontPaths.end()) {
text = VImage::text( text = VImage::text(
".", VImage::option()->set("fontfile", ".", VImage::option()->set("fontfile",
(basePath + findResult->second).c_str())); (basePath + findResult->second).c_str()));
} }
text = VImage::text( text = VImage::text(
captionText.c_str(), captionText.c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("align", VIPS_ALIGN_LOW) ->set("align", VIPS_ALIGN_LOW)
->set("width", textWidth)); ->set("width", textWidth));
VImage captionImage = VImage captionImage =
((text == (vector<double>){0, 0, 0, 0}).bandand()) ((text == (vector<double>){0, 0, 0, 0}).bandand())
.ifthenelse(255, text) .ifthenelse(255, text)
.embed(width / 25, width / 25, width, text.height() + size, .embed(width / 25, width / 25, width, text.height() + size,
VImage::option()->set("extend", "white")); VImage::option()->set("extend", "white"));
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage frame = VImage frame =
(top ? captionImage : img_frame) (top ? captionImage : img_frame)
.join(top ? img_frame : captionImage, VIPS_DIRECTION_VERTICAL, .join(top ? img_frame : captionImage, VIPS_DIRECTION_VERTICAL,
VImage::option() VImage::option()
->set("background", 0xffffff) ->set("background", 0xffffff)
->set("expand", true)); ->set("expand", true));
img.push_back(frame); img.push_back(frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height()); final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height());
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* CaptionTwo(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap CaptionTwo(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,54 +1,60 @@
#include <Magick++.h> #include <Magick++.h>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include <map> #include <map>
#include <string> #include <string>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace Magick; using namespace Magick;
char *Circle(string type, string *outType, char *BufferData, ArgumentMap Circle(string type, string *outType, char *BufferData,
size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments,
size_t *DataSize) { size_t *DataSize) {
Blob blob; Blob blob;
list<Image> frames; list<Image> frames;
list<Image> coalesced; list<Image> coalesced;
list<Image> blurred; list<Image> blurred;
try { try {
readImages(&frames, Blob(BufferData, BufferLength)); readImages(&frames, Blob(BufferData, BufferLength));
} catch (Magick::WarningCoder &warning) { } catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl; cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) { } catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl; cerr << "Warning: " << warning.what() << endl;
} }
coalesceImages(&coalesced, frames.begin(), frames.end()); coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) { for (Image &image : coalesced) {
image.rotationalBlur(10); image.rotationalBlur(10);
image.magick(*outType); image.magick(*outType);
blurred.push_back(image); blurred.push_back(image);
} }
optimizeTransparency(blurred.begin(), blurred.end()); optimizeTransparency(blurred.begin(), blurred.end());
if (*outType == "gif") { if (*outType == "gif") {
for (Image &image : blurred) { for (Image &image : blurred) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod); image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize(); image.quantize();
} }
} }
writeImages(blurred.begin(), blurred.end(), &blob); writeImages(blurred.begin(), blurred.end(), &blob);
*DataSize = blob.length(); *DataSize = blob.length();
// workaround because the data is tied to the blob // workaround because the data is tied to the blob
char *data = (char *)malloc(*DataSize); char *data = (char *)malloc(*DataSize);
memcpy(data, blob.data(), *DataSize); memcpy(data, blob.data(), *DataSize);
return data;
ArgumentMap output;
output["buf"] = data;
output["width"] = (int)blurred.front().columns();
output["height"] = (int)blurred.front().rows();
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Circle(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Circle(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,26 +1,26 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include "../common.h" #include "../common.h"
void showUsage(char *path) { void showUsage(char *path) {
std::cout << "Usage: " << path << " operation [--arg=\"param\"] [...]" << std::endl; std::cout << "Usage: " << path << " operation [--arg=\"param\"] [...]" << std::endl;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (argc < 1 || if (argc < 1 ||
(argc == 1 && !strcmp(argv[1], "-h"))) { (argc == 1 && !strcmp(argv[1], "-h"))) {
showUsage(argv[0]); showUsage(argv[0]);
#ifdef _WIN32 #ifdef _WIN32
system("PAUSE"); system("PAUSE");
#endif #endif
return 1; return 1;
} }
char *op = argv[1]; char *op = argv[1];
//handleArguments(argc, argv); //handleArguments(argc, argv);
std::cout << "This does nothing yet, but it might in the future!" << std::endl; std::cout << "This does nothing yet, but it might in the future!" << std::endl;
return 0; return 0;
} }

View file

@ -1,36 +1,41 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
VImage sepia = VImage::new_matrixv(3, 3, 0.3588, 0.7044, 0.1368, 0.2990, 0.5870, VImage sepia = VImage::new_matrixv(3, 3, 0.3588, 0.7044, 0.1368, 0.2990, 0.5870,
0.1140, 0.2392, 0.4696, 0.0912); 0.1140, 0.2392, 0.4696, 0.0912);
char *Colors(string type, string *outType, char *BufferData, ArgumentMap Colors(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string color = GetArgument<string>(Arguments, "color"); string color = GetArgument<string>(Arguments, "color");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
VImage out; VImage out;
if (color == "grayscale") { if (color == "grayscale") {
out = in.colourspace(VIPS_INTERPRETATION_B_W); out = in.colourspace(VIPS_INTERPRETATION_B_W);
} else if (color == "sepia") { } else if (color == "sepia") {
out = in.extract_band(0, VImage::option()->set("n", 3)).recomb(sepia); out = in.extract_band(0, VImage::option()->set("n", 3)).recomb(sepia);
} }
void *buf; void *buf;
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = out.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Colors(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Colors(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,130 +1,130 @@
#pragma once #pragma once
#include <map> #include <map>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <variant> #include <variant>
using std::map; using std::map;
using std::string; using std::string;
using std::variant; using std::variant;
typedef variant<string, float, bool, int> ArgumentVariant; typedef variant<char*, string, float, bool, int> ArgumentVariant;
typedef map<string, ArgumentVariant> ArgumentMap; typedef map<string, ArgumentVariant> ArgumentMap;
#include "blur.h" #include "blur.h"
#include "bounce.h" #include "bounce.h"
#include "caption.h" #include "caption.h"
#include "caption2.h" #include "caption2.h"
#include "circle.h" #include "circle.h"
#include "colors.h" #include "colors.h"
#include "crop.h" #include "crop.h"
#include "deepfry.h" #include "deepfry.h"
#include "explode.h" #include "explode.h"
#include "flag.h" #include "flag.h"
#include "flip.h" #include "flip.h"
#include "freeze.h" #include "freeze.h"
#include "gamexplain.h" #include "gamexplain.h"
#include "globe.h" #include "globe.h"
#include "homebrew.h" #include "homebrew.h"
#include "invert.h" #include "invert.h"
#include "jpeg.h" #include "jpeg.h"
#include "magik.h" #include "magik.h"
#include "meme.h" #include "meme.h"
#include "mirror.h" #include "mirror.h"
#include "motivate.h" #include "motivate.h"
#include "reddit.h" #include "reddit.h"
#include "resize.h" #include "resize.h"
#include "reverse.h" #include "reverse.h"
#include "scott.h" #include "scott.h"
#include "snapchat.h" #include "snapchat.h"
#include "sonic.h" #include "sonic.h"
#include "speed.h" #include "speed.h"
#include "spin.h" #include "spin.h"
#include "squish.h" #include "squish.h"
#include "swirl.h" #include "swirl.h"
#include "tile.h" #include "tile.h"
#include "togif.h" #include "togif.h"
#include "uncanny.h" #include "uncanny.h"
#include "uncaption.h" #include "uncaption.h"
#include "wall.h" #include "wall.h"
#include "watermark.h" #include "watermark.h"
#include "whisper.h" #include "whisper.h"
#include "zamn.h" #include "zamn.h"
template <typename T> template <typename T>
T GetArgument(ArgumentMap map, string key) { T GetArgument(ArgumentMap map, string key) {
try { try {
return std::get<T>(map.at(key)); return std::get<T>(map.at(key));
} catch (std::bad_variant_access&) { } catch (std::bad_variant_access&) {
throw "Invalid requested type from variant."; throw "Invalid requested type from variant.";
} }
} }
template <typename T> template <typename T>
T GetArgumentWithFallback(ArgumentMap map, string key, T fallback) { T GetArgumentWithFallback(ArgumentMap map, string key, T fallback) {
try { try {
return std::get<T>(map.at(key)); return std::get<T>(map.at(key));
} catch (...) { // this is, not great... } catch (...) { // this is, not great...
return fallback; return fallback;
} }
} }
#define MAP_HAS(ARRAY, KEY) (ARRAY.count(KEY) > 0) #define MAP_HAS(ARRAY, KEY) (ARRAY.count(KEY) > 0)
#define MAP_GET(ARRAY, KEY, TYPE) \ #define MAP_GET(ARRAY, KEY, TYPE) \
(MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) \ (MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) \
: NULL) // C++ has forced my hand : NULL) // C++ has forced my hand
#define MAP_GET_FALLBACK(ARRAY, KEY, TYPE, FALLBACK) \ #define MAP_GET_FALLBACK(ARRAY, KEY, TYPE, FALLBACK) \
(MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) : FALLBACK) (MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) : FALLBACK)
#define ARG_TYPES std::variant<string, bool, int, float> #define ARG_TYPES std::variant<string, bool, int, float>
const std::unordered_map<std::string, std::string> fontPaths{ const std::unordered_map<std::string, std::string> fontPaths{
{"futura", "assets/fonts/caption.otf"}, {"futura", "assets/fonts/caption.otf"},
{"helvetica", "assets/fonts/caption2.ttf"}, {"helvetica", "assets/fonts/caption2.ttf"},
{"roboto", "assets/fonts/reddit.ttf"}}; {"roboto", "assets/fonts/reddit.ttf"}};
const std::map<std::string, const std::map<std::string,
char* (*)(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap (*)(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize)> ArgumentMap Arguments, size_t* DataSize)>
FunctionMap = {{"blur", &Blur}, FunctionMap = {{"blur", &Blur},
{"bounce", &Bounce}, {"bounce", &Bounce},
{"caption", &Caption}, {"caption", &Caption},
{"captionTwo", &CaptionTwo}, {"captionTwo", &CaptionTwo},
{"circle", &Circle}, {"circle", &Circle},
{"colors", &Colors}, {"colors", &Colors},
{"crop", &Crop}, {"crop", &Crop},
{"deepfry", &Deepfry}, {"deepfry", &Deepfry},
{"explode", &Explode}, {"explode", &Explode},
{"flag", &Flag}, {"flag", &Flag},
{"flip", &Flip}, {"flip", &Flip},
{"freeze", &Freeze}, {"freeze", &Freeze},
{"gamexplain", Gamexplain}, {"gamexplain", Gamexplain},
{"globe", Globe}, {"globe", Globe},
{"invert", Invert}, {"invert", Invert},
{"jpeg", Jpeg}, {"jpeg", Jpeg},
{"magik", Magik}, {"magik", Magik},
{"meme", Meme}, {"meme", Meme},
{"mirror", Mirror}, {"mirror", Mirror},
{"motivate", Motivate}, {"motivate", Motivate},
{"reddit", Reddit}, {"reddit", Reddit},
{"resize", Resize}, {"resize", Resize},
{"reverse", Reverse}, {"reverse", Reverse},
{"scott", Scott}, {"scott", Scott},
{"snapchat", Snapchat}, {"snapchat", Snapchat},
{"speed", &Speed}, {"speed", &Speed},
{"spin", Spin}, {"spin", Spin},
{"squish", Squish}, {"squish", Squish},
{"swirl", Swirl}, {"swirl", Swirl},
{"tile", Tile}, {"tile", Tile},
{"togif", ToGif}, {"togif", ToGif},
{"uncanny", Uncanny}, {"uncanny", Uncanny},
{"uncaption", &Uncaption}, {"uncaption", &Uncaption},
{"wall", Wall}, {"wall", Wall},
{"watermark", &Watermark}, {"watermark", &Watermark},
{"whisper", Whisper}, {"whisper", Whisper},
{"zamn", Zamn}}; {"zamn", Zamn}};
const std::map<std::string, const std::map<std::string,
char* (*)(string type, string* outType, ArgumentMap Arguments, size_t* DataSize)> ArgumentMap (*)(string type, string* outType, ArgumentMap Arguments, size_t* DataSize)>
NoInputFunctionMap = {{"homebrew", Homebrew}, {"sonic", Sonic}}; NoInputFunctionMap = {{"homebrew", Homebrew}, {"sonic", Sonic}};

View file

@ -1,51 +1,56 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Crop(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Crop(string type, string *outType, char *BufferData, size_t BufferLength,
[[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) {
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
vector<VImage> img; vector<VImage> img;
int finalHeight = 0; int finalHeight = 0;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
int frameWidth = img_frame.width(); int frameWidth = img_frame.width();
int frameHeight = img_frame.height(); int frameHeight = img_frame.height();
bool widthOrHeight = frameWidth / frameHeight >= 1; bool widthOrHeight = frameWidth / frameHeight >= 1;
int size = widthOrHeight ? frameHeight : frameWidth; int size = widthOrHeight ? frameHeight : frameWidth;
// img_frame.crop(frameWidth - size, frameHeight - size, size, size); // img_frame.crop(frameWidth - size, frameHeight - size, size, size);
VImage result = img_frame.smartcrop( VImage result = img_frame.smartcrop(
size, size, size, size,
VImage::option()->set("interesting", VIPS_INTERESTING_CENTRE)); VImage::option()->set("interesting", VIPS_INTERESTING_CENTRE));
finalHeight = size; finalHeight = size;
img.push_back(result); img.push_back(result);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, finalHeight); final.set(VIPS_META_PAGE_HEIGHT, finalHeight);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = finalHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Crop(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Crop(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,61 +1,66 @@
#include <map> #include <map>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Deepfry(string type, string *outType, char *BufferData, ArgumentMap Deepfry(string type, string *outType, char *BufferData,
size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments,
size_t *DataSize) { size_t *DataSize) {
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int totalHeight = in.height(); int totalHeight = in.height();
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
VImage fried = (in * 1.3 - (255.0 * 1.3 - 255.0)) * 1.5; VImage fried = (in * 1.3 - (255.0 * 1.3 - 255.0)) * 1.5;
VImage final; VImage final;
if (totalHeight > 65500 && type == "gif") { if (totalHeight > 65500 && type == "gif") {
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight);
void *jpgBuf; void *jpgBuf;
size_t jpgLength; size_t jpgLength;
img_frame.write_to_buffer( img_frame.write_to_buffer(
".jpg", &jpgBuf, &jpgLength, ".jpg", &jpgBuf, &jpgLength,
VImage::option()->set("Q", 1)->set("strip", true)); VImage::option()->set("Q", 1)->set("strip", true));
VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, "");
jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight);
jpeged.set("delay", in.get_array_int("delay")); jpeged.set("delay", in.get_array_int("delay"));
img.push_back(jpeged); img.push_back(jpeged);
} }
final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
} else { } else {
void *jpgBuf; void *jpgBuf;
size_t jpgLength; size_t jpgLength;
fried.write_to_buffer(".jpg", &jpgBuf, &jpgLength, fried.write_to_buffer(".jpg", &jpgBuf, &jpgLength,
VImage::option()->set("Q", 1)->set("strip", true)); VImage::option()->set("Q", 1)->set("strip", true));
final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); final = VImage::new_from_buffer(jpgBuf, jpgLength, "");
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
if (type == "gif") final.set("delay", fried.get_array_int("delay")); if (type == "gif") final.set("delay", fried.get_array_int("delay"));
} }
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" ? VImage::option()->set("dither", 0) : 0); *outType == "gif" ? VImage::option()->set("dither", 0) : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Deepfry(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Deepfry(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,52 +1,57 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Explode(string type, string *outType, char *BufferData, ArgumentMap Explode(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
bool implode = GetArgumentWithFallback<bool>(Arguments, "implode", false); bool implode = GetArgumentWithFallback<bool>(Arguments, "implode", false);
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option(); VOption *options = VImage::option();
VImage in = VImage in =
VImage::new_from_buffer( VImage::new_from_buffer(
BufferData, BufferLength, "", BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1)->set("access", "sequential") type == "gif" ? options->set("n", -1)->set("access", "sequential")
: options) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
string distortPath = basePath + "assets/images/" + string distortPath = basePath + "assets/images/" +
(implode ? "linearimplode.png" : "linearexplode.png"); (implode ? "linearimplode.png" : "linearexplode.png");
VImage distort = VImage distort =
(VImage::new_from_file(distortPath.c_str()) (VImage::new_from_file(distortPath.c_str())
.resize(width / 500.0, VImage::option() .resize(width / 500.0, VImage::option()
->set("vscale", pageHeight / 500.0) ->set("vscale", pageHeight / 500.0)
->set("kernel", VIPS_KERNEL_CUBIC)) / ->set("kernel", VIPS_KERNEL_CUBIC)) /
65535); 65535);
VImage distortImage = (distort[0] * width).bandjoin(distort[1] * pageHeight); VImage distortImage = (distort[0] * width).bandjoin(distort[1] * pageHeight);
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage mapped = img_frame.mapim(distortImage); VImage mapped = img_frame.mapim(distortImage);
img.push_back(mapped); img.push_back(mapped);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
void *buf; void *buf;
final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Explode(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Explode(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,49 +1,54 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Flag(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Flag(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
string overlay = GetArgument<string>(Arguments, "overlay"); string overlay = GetArgument<string>(Arguments, "overlay");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
string assetPath = basePath + overlay; string assetPath = basePath + overlay;
VImage overlayInput = VImage::new_from_file(assetPath.c_str()); VImage overlayInput = VImage::new_from_file(assetPath.c_str());
VImage overlayImage = overlayInput.resize( VImage overlayImage = overlayInput.resize(
(double)width / (double)overlayInput.width(), (double)width / (double)overlayInput.width(),
VImage::option()->set( VImage::option()->set(
"vscale", (double)pageHeight / (double)overlayInput.height())); "vscale", (double)pageHeight / (double)overlayInput.height()));
if (!overlayImage.has_alpha()) { if (!overlayImage.has_alpha()) {
overlayImage = overlayImage.bandjoin(127); overlayImage = overlayImage.bandjoin(127);
} else { } else {
// this is a pretty cool line, just saying // this is a pretty cool line, just saying
overlayImage = overlayImage * vector<double>{1, 1, 1, 0.5}; overlayImage = overlayImage * vector<double>{1, 1, 1, 0.5};
} }
VImage replicated = overlayImage.replicate(1, nPages); VImage replicated = overlayImage.replicate(1, nPages);
VImage final = in.composite2(replicated, VIPS_BLEND_MODE_OVER); VImage final = in.composite2(replicated, VIPS_BLEND_MODE_OVER);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Flag(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Flag(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,48 +1,53 @@
#include <map> #include <map>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Flip(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Flip(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
bool flop = GetArgument<bool>(Arguments, "flop"); bool flop = GetArgumentWithFallback<bool>(Arguments, "flop", false);
VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", VImage in = VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" type == "gif"
? VImage::option()->set("n", -1)->set( ? VImage::option()->set("n", -1)->set(
"access", "sequential") "access", "sequential")
: 0) : 0)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
VImage out; VImage out;
if (flop) { if (flop) {
out = in.flip(VIPS_DIRECTION_HORIZONTAL); out = in.flip(VIPS_DIRECTION_HORIZONTAL);
} else if (type == "gif") { } else if (type == "gif") {
// libvips gif handling is both a blessing and a curse // libvips gif handling is both a blessing and a curse
vector<VImage> img; vector<VImage> img;
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = in.crop(0, i * pageHeight, in.width(), pageHeight); VImage img_frame = in.crop(0, i * pageHeight, in.width(), pageHeight);
VImage flipped = img_frame.flip(VIPS_DIRECTION_VERTICAL); VImage flipped = img_frame.flip(VIPS_DIRECTION_VERTICAL);
img.push_back(flipped); img.push_back(flipped);
} }
out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); out = VImage::arrayjoin(img, VImage::option()->set("across", 1));
out.set(VIPS_META_PAGE_HEIGHT, pageHeight); out.set(VIPS_META_PAGE_HEIGHT, pageHeight);
} else { } else {
out = in.flip(VIPS_DIRECTION_VERTICAL); out = in.flip(VIPS_DIRECTION_VERTICAL);
} }
void *buf; void *buf;
out.write_to_buffer( out.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = out.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Flip(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Flip(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,83 +1,107 @@
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Freeze(string type, string *outType, char *BufferData, ArgumentMap Freeze(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
bool loop = GetArgumentWithFallback<bool>(Arguments, "loop", false); bool loop = GetArgumentWithFallback<bool>(Arguments, "loop", false);
int frame = GetArgumentWithFallback<int>(Arguments, "frame", -1); int frame = GetArgumentWithFallback<int>(Arguments, "frame", -1);
char *fileData = (char *)malloc(BufferLength); char *fileData = (char *)malloc(BufferLength);
memcpy(fileData, BufferData, BufferLength); memcpy(fileData, BufferData, BufferLength);
char *match = (char *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01"; char *match = (char *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01";
char *descriptor = (char *)"\x2C\x00\x00\x00\x00"; char *descriptor = (char *)"\x2C\x00\x00\x00\x00";
char *lastPos; char *lastPos;
bool none = true; bool none = true;
if (loop) { if (loop) {
char *newData = (char *)malloc(BufferLength + 19); char *newData = (char *)malloc(BufferLength + 19);
memcpy(newData, fileData, BufferLength); memcpy(newData, fileData, BufferLength);
lastPos = (char *)memchr(newData, '\x2C', BufferLength); lastPos = (char *)memchr(newData, '\x2C', BufferLength);
while (lastPos != NULL) { while (lastPos != NULL) {
if (memcmp(lastPos, descriptor, 5) != 0) { if (memcmp(lastPos, descriptor, 5) != 0) {
lastPos = (char *)memchr(lastPos + 1, '\x2C', lastPos = (char *)memchr(lastPos + 1, '\x2C',
(BufferLength - (lastPos - newData)) - 1); (BufferLength - (lastPos - newData)) - 1);
continue; continue;
} }
memcpy(lastPos + 19, lastPos, (BufferLength - (lastPos - newData))); memcpy(lastPos + 19, lastPos, (BufferLength - (lastPos - newData)));
memcpy(lastPos, match, 16); memcpy(lastPos, match, 16);
memcpy(lastPos + 16, "\x00\x00\x00", 3); memcpy(lastPos + 16, "\x00\x00\x00", 3);
none = false; none = false;
*DataSize = BufferLength + 19; *DataSize = BufferLength + 19;
break; break;
} }
if (none) *DataSize = BufferLength; if (none) *DataSize = BufferLength;
return newData; VImage in =
} else if (frame >= 0 && !loop) { VImage::new_from_buffer(newData, *DataSize, "",
VOption *options = VImage::option()->set("access", "sequential"); VImage::option()->set("access", "sequential"));
VImage in = ArgumentMap output;
VImage::new_from_buffer(BufferData, BufferLength, "", output["buf"] = newData;
type == "gif" ? options->set("n", -1) : options) output["width"] = in.width();
.colourspace(VIPS_INTERPRETATION_sRGB); output["height"] = vips_image_get_page_height(in.get_image());
if (!in.has_alpha()) in = in.bandjoin(255);
return output;
int pageHeight = vips_image_get_page_height(in.get_image()); } else if (frame >= 0 && !loop) {
int nPages = vips_image_get_n_pages(in.get_image()); VOption *options = VImage::option()->set("access", "sequential");
int framePos = clamp(frame, 0, (int)nPages);
VImage out = in.crop(0, 0, in.width(), pageHeight * (framePos + 1)); VImage in =
out.set(VIPS_META_PAGE_HEIGHT, pageHeight); VImage::new_from_buffer(BufferData, BufferLength, "",
out.set("loop", 1); type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
void *buf; if (!in.has_alpha()) in = in.bandjoin(255);
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
int pageHeight = vips_image_get_page_height(in.get_image());
return (char *)buf; int nPages = vips_image_get_n_pages(in.get_image());
} else { int framePos = clamp(frame, 0, (int)nPages);
lastPos = (char *)memchr(fileData, '\x21', BufferLength); VImage out = in.crop(0, 0, in.width(), pageHeight * (framePos + 1));
while (lastPos != NULL) { out.set(VIPS_META_PAGE_HEIGHT, pageHeight);
if (memcmp(lastPos, match, 16) != 0) { out.set("loop", 1);
lastPos = (char *)memchr(lastPos + 1, '\x21',
(BufferLength - (lastPos - fileData)) - 1); void *buf;
continue; out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
}
memcpy(lastPos, lastPos + 19, (BufferLength - (lastPos - fileData)) - 19); ArgumentMap output;
*DataSize = BufferLength - 19; output["buf"] = (char *)buf;
none = false; output["width"] = out.width();
break; output["height"] = vips_image_get_page_height(in.get_image());
}
if (none) *DataSize = BufferLength; return output;
return fileData; } else {
} lastPos = (char *)memchr(fileData, '\x21', BufferLength);
} while (lastPos != NULL) {
if (memcmp(lastPos, match, 16) != 0) {
lastPos = (char *)memchr(lastPos + 1, '\x21',
(BufferLength - (lastPos - fileData)) - 1);
continue;
}
memcpy(lastPos, lastPos + 19, (BufferLength - (lastPos - fileData)) - 19);
*DataSize = BufferLength - 19;
none = false;
break;
}
if (none) *DataSize = BufferLength;
VImage in =
VImage::new_from_buffer(fileData, *DataSize, "",
VImage::option()->set("access", "sequential"));
ArgumentMap output;
output["buf"] = fileData;
output["width"] = in.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Freeze(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Freeze(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,50 +1,55 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Gamexplain(string type, string *outType, char *BufferData, ArgumentMap Gamexplain(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
string assetPath = basePath + "assets/images/gamexplain.png"; string assetPath = basePath + "assets/images/gamexplain.png";
VImage tmpl = VImage::new_from_file(assetPath.c_str()); VImage tmpl = VImage::new_from_file(assetPath.c_str());
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage resized = VImage resized =
img_frame img_frame
.resize(1181.0 / (double)width, .resize(1181.0 / (double)width,
VImage::option()->set("vscale", 571.0 / (double)pageHeight)) VImage::option()->set("vscale", 571.0 / (double)pageHeight))
.embed(10, 92, 1200, 675, VImage::option()->set("extend", "white")); .embed(10, 92, 1200, 675, VImage::option()->set("extend", "white"));
VImage composited = resized.composite2(tmpl, VIPS_BLEND_MODE_OVER); VImage composited = resized.composite2(tmpl, VIPS_BLEND_MODE_OVER);
img.push_back(composited); img.push_back(composited);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, 675); final.set(VIPS_META_PAGE_HEIGHT, 675);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Gamexplain(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Gamexplain(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,73 +1,78 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Globe(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Globe(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option(); VOption *options = VImage::option();
VImage in = VImage in =
VImage::new_from_buffer( VImage::new_from_buffer(
BufferData, BufferLength, "", BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1)->set("access", "sequential") type == "gif" ? options->set("n", -1)->set("access", "sequential")
: options) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30;
double size = min(width, pageHeight); double size = min(width, pageHeight);
string diffPath = basePath + "assets/images/globediffuse.png"; string diffPath = basePath + "assets/images/globediffuse.png";
VImage diffuse = VImage::new_from_file(diffPath.c_str()) VImage diffuse = VImage::new_from_file(diffPath.c_str())
.resize(size / 500.0, VImage::option()->set( .resize(size / 500.0, VImage::option()->set(
"kernel", VIPS_KERNEL_CUBIC)) / "kernel", VIPS_KERNEL_CUBIC)) /
255; 255;
string specPath = basePath + "assets/images/globespec.png"; string specPath = basePath + "assets/images/globespec.png";
VImage specular = VImage::new_from_file(specPath.c_str()) VImage specular = VImage::new_from_file(specPath.c_str())
.resize(size / 500.0, VImage::option()->set( .resize(size / 500.0, VImage::option()->set(
"kernel", VIPS_KERNEL_CUBIC)); "kernel", VIPS_KERNEL_CUBIC));
string distortPath = basePath + "assets/images/spheremap.png"; string distortPath = basePath + "assets/images/spheremap.png";
VImage distort = VImage distort =
(VImage::new_from_file(distortPath.c_str()) (VImage::new_from_file(distortPath.c_str())
.resize(size / 500.0, .resize(size / 500.0,
VImage::option()->set("kernel", VIPS_KERNEL_CUBIC)) / VImage::option()->set("kernel", VIPS_KERNEL_CUBIC)) /
65535) * 65535) *
size; size;
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage resized = img_frame.resize( VImage resized = img_frame.resize(
size / (double)width, size / (double)width,
VImage::option()->set("vscale", size / (double)pageHeight)); VImage::option()->set("vscale", size / (double)pageHeight));
VImage rolled = img_frame.wrap( VImage rolled = img_frame.wrap(
VImage::option()->set("x", width * i / nPages)->set("y", 0)); VImage::option()->set("x", width * i / nPages)->set("y", 0));
VImage extracted = rolled.extract_band(0, VImage::option()->set("n", 3)); VImage extracted = rolled.extract_band(0, VImage::option()->set("n", 3));
VImage mapped = extracted.mapim(distort); VImage mapped = extracted.mapim(distort);
VImage composited = mapped * diffuse + specular; VImage composited = mapped * diffuse + specular;
VImage frame = composited.bandjoin(diffuse > 0.0); VImage frame = composited.bandjoin(diffuse > 0.0);
img.push_back(frame); img.push_back(frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, size); final.set(VIPS_META_PAGE_HEIGHT, size);
if (type != "gif") { if (type != "gif") {
vector<int> delay(30, 50); vector<int> delay(30, 50);
final.set("delay", delay); final.set("delay", delay);
} }
void *buf; void *buf;
final.write_to_buffer(".gif", &buf, DataSize); final.write_to_buffer(".gif", &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Globe(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Globe(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,37 +1,42 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Homebrew(string type, string *outType, ArgumentMap Arguments, ArgumentMap Homebrew(string type, string *outType, ArgumentMap Arguments,
size_t *DataSize) { size_t *DataSize) {
string caption = GetArgument<string>(Arguments, "caption"); string caption = GetArgument<string>(Arguments, "caption");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
string assetPath = basePath + "assets/images/hbc.png"; string assetPath = basePath + "assets/images/hbc.png";
VImage bg = VImage::new_from_file(assetPath.c_str()); VImage bg = VImage::new_from_file(assetPath.c_str());
VImage text = VImage::text( VImage text = VImage::text(
".", VImage::option()->set("fontfile", ".", VImage::option()->set("fontfile",
(basePath + "assets/fonts/hbc.ttf").c_str())); (basePath + "assets/fonts/hbc.ttf").c_str()));
text = VImage::text( text = VImage::text(
("<span letter_spacing=\"-5120\" color=\"white\">" + caption + "</span>") ("<span letter_spacing=\"-5120\" color=\"white\">" + caption + "</span>")
.c_str(), .c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", "PF Square Sans Pro, Twemoji Color Font 96") ->set("font", "PF Square Sans Pro, Twemoji Color Font 96")
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())); ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()));
VImage out = bg.composite2(text, VIPS_BLEND_MODE_OVER, VImage out = bg.composite2(text, VIPS_BLEND_MODE_OVER,
VImage::option() VImage::option()
->set("x", 400 - (text.width() / 2)) ->set("x", 400 - (text.width() / 2))
->set("y", 300 - (text.height() / 2) - 8)); ->set("y", 300 - (text.height() / 2) - 8));
void *buf; void *buf;
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = bg.width();
output["height"] = vips_image_get_page_height(bg.get_image());
return output;
} }

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char *Homebrew(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); ArgumentMap Homebrew(string type, string *outType, ArgumentMap Arguments, size_t *DataSize);

View file

@ -1,28 +1,33 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Invert(string type, string *outType, char *BufferData, ArgumentMap Invert(string type, string *outType, char *BufferData,
size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments,
size_t *DataSize) { size_t *DataSize) {
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
VImage noAlpha = VImage noAlpha =
in.extract_band(0, VImage::option()->set("n", in.bands() - 1)); in.extract_band(0, VImage::option()->set("n", in.bands() - 1));
VImage inverted = noAlpha.invert(); VImage inverted = noAlpha.invert();
VImage out = inverted.bandjoin(in.extract_band(3)); VImage out = inverted.bandjoin(in.extract_band(3));
void *buf; void *buf;
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = out.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Invert(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Invert(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,75 +1,83 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Jpeg(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Jpeg(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
int quality = GetArgumentWithFallback<int>(Arguments, "quality", 0); int quality = GetArgumentWithFallback<int>(Arguments, "quality", 0);
void *buf; void *buf;
ArgumentMap output;
if (type == "gif") {
VImage in = VImage::new_from_buffer(
BufferData, BufferLength, "", if (type == "gif") {
VImage::option()->set("access", "sequential")->set("n", -1)) VImage in = VImage::new_from_buffer(
.colourspace(VIPS_INTERPRETATION_sRGB); BufferData, BufferLength, "",
if (!in.has_alpha()) in = in.bandjoin(255); VImage::option()->set("access", "sequential")->set("n", -1))
.colourspace(VIPS_INTERPRETATION_sRGB);
int width = in.width(); if (!in.has_alpha()) in = in.bandjoin(255);
int pageHeight = vips_image_get_page_height(in.get_image());
int totalHeight = in.height(); int width = in.width();
int nPages = vips_image_get_n_pages(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int totalHeight = in.height();
VImage final; int nPages = vips_image_get_n_pages(in.get_image());
if (totalHeight > 65500) { VImage final;
vector<VImage> img;
for (int i = 0; i < nPages; i++) { if (totalHeight > 65500) {
VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); vector<VImage> img;
void *jpgBuf; for (int i = 0; i < nPages; i++) {
size_t jpgLength; VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight);
img_frame.write_to_buffer( void *jpgBuf;
".jpg", &jpgBuf, &jpgLength, size_t jpgLength;
VImage::option()->set("Q", quality)->set("strip", true)); img_frame.write_to_buffer(
VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); ".jpg", &jpgBuf, &jpgLength,
jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); VImage::option()->set("Q", quality)->set("strip", true));
jpeged.set("delay", in.get_array_int("delay")); VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, "");
img.push_back(jpeged); jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight);
} jpeged.set("delay", in.get_array_int("delay"));
final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); img.push_back(jpeged);
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); }
} else { final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
void *jpgBuf; final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
size_t jpgLength; } else {
in.write_to_buffer( void *jpgBuf;
".jpg", &jpgBuf, &jpgLength, size_t jpgLength;
VImage::option()->set("Q", quality)->set("strip", true)); in.write_to_buffer(
final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); ".jpg", &jpgBuf, &jpgLength,
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); VImage::option()->set("Q", quality)->set("strip", true));
final.set("delay", in.get_array_int("delay")); final = VImage::new_from_buffer(jpgBuf, jpgLength, "");
} final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
final.set("delay", in.get_array_int("delay"));
final.write_to_buffer( }
("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" ? VImage::option()->set("dither", 0) : 0); final.write_to_buffer(
} else { ("." + *outType).c_str(), &buf, DataSize,
VImage in = VImage::new_from_buffer(BufferData, BufferLength, ""); *outType == "gif" ? VImage::option()->set("dither", 0) : 0);
void *jpgBuf; output["buf"] = (char *)buf;
in.write_to_buffer(".jpg", &jpgBuf, DataSize, output["width"] = width;
VImage::option()->set("Q", quality)->set("strip", true)); output["height"] = pageHeight;
if (*outType == "gif") { } else {
VImage gifIn = VImage::new_from_buffer((char *)jpgBuf, *DataSize, ""); VImage in = VImage::new_from_buffer(BufferData, BufferLength, "");
gifIn.write_to_buffer( void *jpgBuf;
".gif", &buf, DataSize, in.write_to_buffer(".jpg", &jpgBuf, DataSize,
VImage::option()->set("Q", quality)->set("strip", true)); VImage::option()->set("Q", quality)->set("strip", true));
} else { if (*outType == "gif") {
*outType = "jpg"; VImage gifIn = VImage::new_from_buffer((char *)jpgBuf, *DataSize, "");
buf = jpgBuf; gifIn.write_to_buffer(
} ".gif", &buf, DataSize,
} VImage::option()->set("Q", quality)->set("strip", true));
} else {
return (char *)buf; *outType = "jpg";
} buf = jpgBuf;
}
output["buf"] = (char *)buf;
output["width"] = in.width();
output["height"] = vips_image_get_page_height(in.get_image());
}
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Jpeg(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Jpeg(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,52 +1,58 @@
#include <Magick++.h> #include <Magick++.h>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace Magick; using namespace Magick;
char *Magik(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Magik(string type, string *outType, char *BufferData, size_t BufferLength,
[[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) {
Blob blob; Blob blob;
list<Image> frames; list<Image> frames;
list<Image> coalesced; list<Image> coalesced;
list<Image> blurred; list<Image> blurred;
try { try {
readImages(&frames, Blob(BufferData, BufferLength)); readImages(&frames, Blob(BufferData, BufferLength));
} catch (Magick::WarningCoder &warning) { } catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl; cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) { } catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl; cerr << "Warning: " << warning.what() << endl;
} }
coalesceImages(&coalesced, frames.begin(), frames.end()); coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) { for (Image &image : coalesced) {
image.scale(Geometry("350x350")); image.scale(Geometry("350x350"));
image.liquidRescale(Geometry("175x175")); image.liquidRescale(Geometry("175x175"));
image.liquidRescale(Geometry("350x350")); image.liquidRescale(Geometry("350x350"));
image.magick(*outType); image.magick(*outType);
blurred.push_back(image); blurred.push_back(image);
} }
optimizeTransparency(blurred.begin(), blurred.end()); optimizeTransparency(blurred.begin(), blurred.end());
if (*outType == "gif") { if (*outType == "gif") {
for (Image &image : blurred) { for (Image &image : blurred) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod); image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize(); image.quantize();
} }
} }
writeImages(blurred.begin(), blurred.end(), &blob); writeImages(blurred.begin(), blurred.end(), &blob);
*DataSize = blob.length(); *DataSize = blob.length();
char *data = (char *)malloc(*DataSize); char *data = (char *)malloc(*DataSize);
memcpy(data, blob.data(), *DataSize); memcpy(data, blob.data(), *DataSize);
return data;
ArgumentMap output;
output["buf"] = data;
output["width"] = (int)blurred.front().columns();
output["height"] = (int)blurred.front().rows();
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Magik(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Magik(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,139 +1,144 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Meme(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Meme(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
string top = GetArgument<string>(Arguments, "top"); string top = GetArgument<string>(Arguments, "top");
string bottom = GetArgument<string>(Arguments, "bottom"); string bottom = GetArgument<string>(Arguments, "bottom");
string font = GetArgument<string>(Arguments, "font"); string font = GetArgument<string>(Arguments, "font");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int size = width / 9; int size = width / 9;
int dividedWidth = width / 1000; int dividedWidth = width / 1000;
int rad = 1; int rad = 1;
vector<double> zeroVec = {0, 0, 0, 0}; vector<double> zeroVec = {0, 0, 0, 0};
string font_string = string font_string =
(font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font " + (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font " +
(font != "impact" ? "bold" : "normal") + " " + to_string(size); (font != "impact" ? "bold" : "normal") + " " + to_string(size);
VImage mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128; VImage mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128;
mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true)); mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true));
VImage altMask; VImage altMask;
if (dividedWidth >= 1) { if (dividedWidth >= 1) {
altMask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128; altMask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128;
altMask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth, altMask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth,
VImage::option()->set("fill", true)); VImage::option()->set("fill", true));
} }
auto findResult = fontPaths.find(font); auto findResult = fontPaths.find(font);
if (findResult != fontPaths.end()) { if (findResult != fontPaths.end()) {
VImage::text(".", VImage::option()->set( VImage::text(".", VImage::option()->set(
"fontfile", (basePath + findResult->second).c_str())); "fontfile", (basePath + findResult->second).c_str()));
} }
VImage topText; VImage topText;
if (top != "") { if (top != "") {
VImage topIn = VImage::text( VImage topIn = VImage::text(
("<span foreground=\"white\">" + top + "</span>").c_str(), ("<span foreground=\"white\">" + top + "</span>").c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", width)); ->set("width", width));
topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20, topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20,
(topIn.height() + 2 * rad) + 20); (topIn.height() + 2 * rad) + 20);
VImage topOutline = VImage topOutline =
topIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) topIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE)
.gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1));
if (dividedWidth >= 1) { if (dividedWidth >= 1) {
topOutline = topOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE); topOutline = topOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE);
} }
topOutline = (topOutline == zeroVec); topOutline = (topOutline == zeroVec);
VImage topInvert = topOutline.extract_band(3).invert(); VImage topInvert = topOutline.extract_band(3).invert();
topOutline = topOutline =
topOutline topOutline
.extract_band(0, VImage::option()->set("n", topOutline.bands() - 1)) .extract_band(0, VImage::option()->set("n", topOutline.bands() - 1))
.bandjoin(topInvert); .bandjoin(topInvert);
topText = topOutline.composite2(topIn, VIPS_BLEND_MODE_OVER); topText = topOutline.composite2(topIn, VIPS_BLEND_MODE_OVER);
} }
VImage bottomText; VImage bottomText;
if (bottom != "") { if (bottom != "") {
VImage bottomIn = VImage::text( VImage bottomIn = VImage::text(
("<span foreground=\"white\">" + bottom + "</span>").c_str(), ("<span foreground=\"white\">" + bottom + "</span>").c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", width)); ->set("width", width));
bottomIn = bottomIn =
bottomIn.embed(rad + 10, rad + 10, (bottomIn.width() + 2 * rad) + 20, bottomIn.embed(rad + 10, rad + 10, (bottomIn.width() + 2 * rad) + 20,
(bottomIn.height() + 2 * rad) + 20); (bottomIn.height() + 2 * rad) + 20);
VImage bottomOutline = VImage bottomOutline =
bottomIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) bottomIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE)
.gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1));
if (dividedWidth >= 1) { if (dividedWidth >= 1) {
bottomOutline = bottomOutline =
bottomOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE); bottomOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE);
} }
bottomOutline = (bottomOutline == zeroVec); bottomOutline = (bottomOutline == zeroVec);
VImage bottomInvert = bottomOutline.extract_band(3).invert(); VImage bottomInvert = bottomOutline.extract_band(3).invert();
bottomOutline = bottomOutline bottomOutline = bottomOutline
.extract_band(0, VImage::option()->set( .extract_band(0, VImage::option()->set(
"n", bottomOutline.bands() - 1)) "n", bottomOutline.bands() - 1))
.bandjoin(bottomInvert); .bandjoin(bottomInvert);
bottomText = bottomOutline.composite2(bottomIn, VIPS_BLEND_MODE_OVER); bottomText = bottomOutline.composite2(bottomIn, VIPS_BLEND_MODE_OVER);
} }
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
if (top != "") { if (top != "") {
img_frame = img_frame.composite2( img_frame = img_frame.composite2(
topText, VIPS_BLEND_MODE_OVER, topText, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", (width / 2) - (topText.width() / 2))); VImage::option()->set("x", (width / 2) - (topText.width() / 2)));
} }
if (bottom != "") { if (bottom != "") {
img_frame = img_frame.composite2( img_frame = img_frame.composite2(
bottomText, VIPS_BLEND_MODE_OVER, bottomText, VIPS_BLEND_MODE_OVER,
VImage::option() VImage::option()
->set("x", (width / 2) - (bottomText.width() / 2)) ->set("x", (width / 2) - (bottomText.width() / 2))
->set("y", pageHeight - bottomText.height())); ->set("y", pageHeight - bottomText.height()));
} }
img.push_back(img_frame); img.push_back(img_frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Meme(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Meme(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,64 +1,69 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Mirror(string type, string *outType, char *BufferData, ArgumentMap Mirror(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
bool vertical = GetArgumentWithFallback<bool>(Arguments, "vertical", false); bool vertical = GetArgumentWithFallback<bool>(Arguments, "vertical", false);
bool first = GetArgumentWithFallback<bool>(Arguments, "first", false); bool first = GetArgumentWithFallback<bool>(Arguments, "first", false);
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
VImage out; VImage out;
if (vertical) { if (vertical) {
if (type == "gif") { if (type == "gif") {
// once again, libvips gif handling is both a blessing and a curse // once again, libvips gif handling is both a blessing and a curse
vector<VImage> img; vector<VImage> img;
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
bool isOdd = pageHeight % 2; bool isOdd = pageHeight % 2;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
int x = (i * pageHeight) + (first ? 0 : (pageHeight / 2)); int x = (i * pageHeight) + (first ? 0 : (pageHeight / 2));
VImage cropped = in.crop(0, x, in.width(), pageHeight / 2); VImage cropped = in.crop(0, x, in.width(), pageHeight / 2);
VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL);
VImage final = VImage::arrayjoin( VImage final = VImage::arrayjoin(
{first ? cropped : flipped, first ? flipped : cropped}, {first ? cropped : flipped, first ? flipped : cropped},
VImage::option()->set("across", 1)); VImage::option()->set("across", 1));
img.push_back(final); img.push_back(final);
} }
out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); out = VImage::arrayjoin(img, VImage::option()->set("across", 1));
out.set(VIPS_META_PAGE_HEIGHT, pageHeight - (isOdd ? 1 : 0)); out.set(VIPS_META_PAGE_HEIGHT, pageHeight - (isOdd ? 1 : 0));
} else { } else {
VImage cropped = in.extract_area(0, 0, in.width(), in.height() / 2); VImage cropped = in.extract_area(0, 0, in.width(), in.height() / 2);
VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL);
out = VImage::arrayjoin({cropped, flipped}, out = VImage::arrayjoin({cropped, flipped},
VImage::option()->set("across", 1)); VImage::option()->set("across", 1));
} }
} else { } else {
if (first) { if (first) {
VImage cropped = in.extract_area(0, 0, in.width() / 2, in.height()); VImage cropped = in.extract_area(0, 0, in.width() / 2, in.height());
VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL);
out = VImage::arrayjoin({cropped, flipped}); out = VImage::arrayjoin({cropped, flipped});
} else { } else {
int size = in.width() / 2; int size = in.width() / 2;
VImage cropped = in.extract_area(size, 0, size, in.height()); VImage cropped = in.extract_area(size, 0, size, in.height());
VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL);
out = VImage::arrayjoin({flipped, cropped}); out = VImage::arrayjoin({flipped, cropped});
} }
} }
void *buf; void *buf;
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = in.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Mirror(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Mirror(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,123 +1,128 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Motivate(string type, string *outType, char *BufferData, ArgumentMap Motivate(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string top_text = GetArgument<string>(Arguments, "top"); string top_text = GetArgument<string>(Arguments, "top");
string bottom_text = GetArgument<string>(Arguments, "bottom"); string bottom_text = GetArgument<string>(Arguments, "bottom");
string font = GetArgument<string>(Arguments, "font"); string font = GetArgument<string>(Arguments, "font");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int size = width / 5; int size = width / 5;
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int textWidth = width - ((width / 25) * 2); int textWidth = width - ((width / 25) * 2);
string font_string = string font_string =
(font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font"; (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font";
auto findResult = fontPaths.find(font); auto findResult = fontPaths.find(font);
if (findResult != fontPaths.end()) { if (findResult != fontPaths.end()) {
VImage::text(".", VImage::option()->set( VImage::text(".", VImage::option()->set(
"fontfile", (basePath + findResult->second).c_str())); "fontfile", (basePath + findResult->second).c_str()));
} }
VImage topImage; VImage topImage;
if (top_text != "") { if (top_text != "") {
string topText = "<span foreground=\"white\" background=\"black\">" + string topText = "<span foreground=\"white\" background=\"black\">" +
top_text + "</span>"; top_text + "</span>";
topImage = VImage::text( topImage = VImage::text(
topText.c_str(), topText.c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", (font_string + " " + to_string(size)).c_str()) ->set("font", (font_string + " " + to_string(size)).c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", textWidth)); ->set("width", textWidth));
} }
VImage bottomImage; VImage bottomImage;
if (bottom_text != "") { if (bottom_text != "") {
string bottomText = "<span foreground=\"white\" background=\"black\">" + string bottomText = "<span foreground=\"white\" background=\"black\">" +
bottom_text + "</span>"; bottom_text + "</span>";
bottomImage = VImage::text( bottomImage = VImage::text(
bottomText.c_str(), bottomText.c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", (font_string + " " + to_string(size * 0.4)).c_str()) ->set("font", (font_string + " " + to_string(size * 0.4)).c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", textWidth)); ->set("width", textWidth));
} }
vector<VImage> img; vector<VImage> img;
int height; int height;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
int borderSize = max(2, width / 66); int borderSize = max(2, width / 66);
int borderSize2 = borderSize * 0.5; int borderSize2 = borderSize * 0.5;
VImage bordered = VImage bordered =
img_frame.embed(borderSize, borderSize, width + (borderSize * 2), img_frame.embed(borderSize, borderSize, width + (borderSize * 2),
pageHeight + (borderSize * 2), pageHeight + (borderSize * 2),
VImage::option()->set("extend", "black")); VImage::option()->set("extend", "black"));
VImage bordered2 = bordered.embed(borderSize2, borderSize2, VImage bordered2 = bordered.embed(borderSize2, borderSize2,
bordered.width() + (borderSize2 * 2), bordered.width() + (borderSize2 * 2),
bordered.height() + (borderSize2 * 2), bordered.height() + (borderSize2 * 2),
VImage::option()->set("extend", "white")); VImage::option()->set("extend", "white"));
int addition = width / 8; int addition = width / 8;
int sideAddition = pageHeight * 0.4; int sideAddition = pageHeight * 0.4;
VImage bordered3 = bordered2.embed( VImage bordered3 = bordered2.embed(
sideAddition / 2, addition / 2, bordered2.width() + sideAddition, sideAddition / 2, addition / 2, bordered2.width() + sideAddition,
bordered2.height() + addition, bordered2.height() + addition,
VImage::option()->set("extend", "black")); VImage::option()->set("extend", "black"));
VImage frame; VImage frame;
if (top_text != "") { if (top_text != "") {
frame = bordered3.join( frame = bordered3.join(
topImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(), topImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(),
topImage.height() + (size / 4), topImage.height() + (size / 4),
VImage::option()->set("extend", "black")), VImage::option()->set("extend", "black")),
VIPS_DIRECTION_VERTICAL, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("background", 0x000000)->set("expand", true)); VImage::option()->set("background", 0x000000)->set("expand", true));
} }
if (bottom_text != "") { if (bottom_text != "") {
if (top_text == "") frame = bordered3; if (top_text == "") frame = bordered3;
frame = frame.join( frame = frame.join(
bottomImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(), bottomImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(),
bottomImage.height() + (size / 4), bottomImage.height() + (size / 4),
VImage::option()->set("extend", "black")), VImage::option()->set("extend", "black")),
VIPS_DIRECTION_VERTICAL, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("background", 0x000000)->set("expand", true)); VImage::option()->set("background", 0x000000)->set("expand", true));
} }
height = frame.height(); height = frame.height();
img.push_back(frame); img.push_back(frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)) VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1))
.extract_band(0, VImage::option()->set("n", 3)); .extract_band(0, VImage::option()->set("n", 3));
final.set(VIPS_META_PAGE_HEIGHT, height); final.set(VIPS_META_PAGE_HEIGHT, height);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" ? VImage::option()->set("dither", 1) : 0); *outType == "gif" ? VImage::option()->set("dither", 1) : 0);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = in.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Motivate(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Motivate(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,124 +1,130 @@
#include <napi.h> #include <napi.h>
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <string> #include <string>
#include "../common.h" #include "../common.h"
#ifdef _WIN32 #ifdef _WIN32
#include <Magick++.h> #include <Magick++.h>
#endif #endif
#include <vips/vips8> #include <vips/vips8>
using namespace std; using namespace std;
bool isNapiValueInt(Napi::Env& env, Napi::Value& num) { bool isNapiValueInt(Napi::Env& env, Napi::Value& num) {
return env.Global() return env.Global()
.Get("Number") .Get("Number")
.ToObject() .ToObject()
.Get("isInteger") .Get("isInteger")
.As<Napi::Function>() .As<Napi::Function>()
.Call({num}) .Call({num})
.ToBoolean() .ToBoolean()
.Value(); .Value();
} }
Napi::Value ProcessImage(const Napi::CallbackInfo& info) { Napi::Value ProcessImage(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env(); Napi::Env env = info.Env();
Napi::Object result = Napi::Object::New(env); Napi::Object result = Napi::Object::New(env);
try { try {
string command = info[0].As<Napi::String>().Utf8Value(); string command = info[0].As<Napi::String>().Utf8Value();
Napi::Object obj = info[1].As<Napi::Object>(); Napi::Object obj = info[1].As<Napi::Object>();
string type = string type =
obj.Has("type") ? obj.Get("type").As<Napi::String>().Utf8Value() : "png"; obj.Has("type") ? obj.Get("type").As<Napi::String>().Utf8Value() : "png";
Napi::Array properties = obj.GetPropertyNames(); Napi::Array properties = obj.GetPropertyNames();
ArgumentMap Arguments; ArgumentMap Arguments;
for (unsigned int i = 0; i < properties.Length(); i++) { for (unsigned int i = 0; i < properties.Length(); i++) {
string property = string property =
properties.Get(uint32_t(i)).As<Napi::String>().Utf8Value(); properties.Get(uint32_t(i)).As<Napi::String>().Utf8Value();
if (property == "data") { if (property == "data") {
continue; continue;
} }
auto val = obj.Get(property); auto val = obj.Get(property);
if (val.IsBoolean()) { if (val.IsBoolean()) {
Arguments[property] = val.ToBoolean().Value(); Arguments[property] = val.ToBoolean().Value();
} else if (val.IsString()) { } else if (val.IsString()) {
Arguments[property] = val.ToString().As<Napi::String>().Utf8Value(); Arguments[property] = val.ToString().As<Napi::String>().Utf8Value();
} else if (val.IsNumber()) { } else if (val.IsNumber()) {
auto num = val.ToNumber(); auto num = val.ToNumber();
if (isNapiValueInt(env, num)) { if (isNapiValueInt(env, num)) {
Arguments[property] = num.Int32Value(); Arguments[property] = num.Int32Value();
} else { } else {
Arguments[property] = num.FloatValue(); Arguments[property] = num.FloatValue();
} }
} else { } else {
throw "Unimplemented value type passed to image native."; throw "Unimplemented value type passed to image native.";
// Arguments[property] = val; // Arguments[property] = val;
} }
} }
string outType = GetArgument<bool>(Arguments, "togif") ? "gif" : type; string outType = GetArgument<bool>(Arguments, "togif") ? "gif" : type;
size_t length = 0; size_t length = 0;
char* buf; ArgumentMap outMap;
if (obj.Has("data")) { if (obj.Has("data")) {
Napi::Buffer<char> data = obj.Has("data") Napi::Buffer<char> data = obj.Has("data")
? obj.Get("data").As<Napi::Buffer<char>>() ? obj.Get("data").As<Napi::Buffer<char>>()
: Napi::Buffer<char>::New(env, 0); : Napi::Buffer<char>::New(env, 0);
buf = FunctionMap.at(command)(type, &outType, data.Data(), data.Length(), outMap = FunctionMap.at(command)(type, &outType, data.Data(), data.Length(),
Arguments, &length); Arguments, &length);
} else { } else {
buf = NoInputFunctionMap.at(command)(type, &outType, Arguments, &length); outMap = NoInputFunctionMap.at(command)(type, &outType, Arguments, &length);
} }
vips_error_clear(); vips_error_clear();
vips_thread_shutdown(); vips_thread_shutdown();
result.Set("data", char* buf = GetArgument<char*>(outMap, "buf");
Napi::Buffer<char>::New(env, buf, length, int width = GetArgument<int>(outMap, "width");
[]([[maybe_unused]] Napi::Env env, int height = GetArgument<int>(outMap, "height");
void* data) { free(data); }));
result.Set("type", outType); result.Set("data",
} catch (std::exception const& err) { Napi::Buffer<char>::New(env, buf, length,
Napi::Error::New(env, err.what()).ThrowAsJavaScriptException(); []([[maybe_unused]] Napi::Env env,
} catch (...) { void* data) { free(data); }));
Napi::Error::New(env, "Unknown error").ThrowAsJavaScriptException(); result.Set("type", outType);
} result.Set("width", width);
result.Set("height", height);
return result; } catch (std::exception const& err) {
} Napi::Error::New(env, err.what()).ThrowAsJavaScriptException();
} catch (...) {
Napi::Object Init(Napi::Env env, Napi::Object exports) { Napi::Error::New(env, "Unknown error").ThrowAsJavaScriptException();
#ifdef _WIN32 }
Magick::InitializeMagick("");
#endif return result;
if (vips_init("")) vips_error_exit(NULL); }
exports.Set(Napi::String::New(env, "image"),
Napi::Function::New(env, ProcessImage)); // new function handler Napi::Object Init(Napi::Env env, Napi::Object exports) {
#ifdef _WIN32
Napi::Array arr = Napi::Array::New(env); Magick::InitializeMagick("");
size_t i = 0; #endif
for (auto const& imap : FunctionMap) { if (vips_init("")) vips_error_exit(NULL);
Napi::HandleScope scope(env); exports.Set(Napi::String::New(env, "image"),
arr[i] = Napi::String::New(env, imap.first); Napi::Function::New(env, ProcessImage)); // new function handler
i++;
} Napi::Array arr = Napi::Array::New(env);
for (auto const& imap : NoInputFunctionMap) { size_t i = 0;
Napi::HandleScope scope(env); for (auto const& imap : FunctionMap) {
arr[i] = Napi::String::New(env, imap.first); Napi::HandleScope scope(env);
i++; arr[i] = Napi::String::New(env, imap.first);
} i++;
}
exports.Set(Napi::String::New(env, "funcs"), arr); for (auto const& imap : NoInputFunctionMap) {
Napi::HandleScope scope(env);
return exports; arr[i] = Napi::String::New(env, imap.first);
} i++;
}
NODE_API_MODULE(addon, Init)
exports.Set(Napi::String::New(env, "funcs"), arr);
return exports;
}
NODE_API_MODULE(addon, Init)

View file

@ -1,67 +1,72 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Reddit(string type, string *outType, char *BufferData, ArgumentMap Reddit(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string text = GetArgumentWithFallback<string>(Arguments, "text", ""); string text = GetArgument<string>(Arguments, "caption");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
string assetPath = basePath + "assets/images/reddit.png"; string assetPath = basePath + "assets/images/reddit.png";
VImage tmpl = VImage::new_from_file(assetPath.c_str()); VImage tmpl = VImage::new_from_file(assetPath.c_str());
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
string captionText = "<span foreground=\"white\">" + text + "</span>"; string captionText = "<span foreground=\"white\">" + text + "</span>";
VImage textImage = VImage::text( VImage textImage = VImage::text(
".", VImage::option()->set( ".", VImage::option()->set(
"fontfile", (basePath + "assets/fonts/reddit.ttf").c_str())); "fontfile", (basePath + "assets/fonts/reddit.ttf").c_str()));
textImage = VImage::text( textImage = VImage::text(
captionText.c_str(), captionText.c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("font", "Roboto, Twemoji Color Font 62") ->set("font", "Roboto, Twemoji Color Font 62")
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("align", VIPS_ALIGN_LOW)); ->set("align", VIPS_ALIGN_LOW));
VImage composited = VImage composited =
tmpl.composite2(textImage, VIPS_BLEND_MODE_OVER, tmpl.composite2(textImage, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", 375)->set( VImage::option()->set("x", 375)->set(
"y", (tmpl.height() - textImage.height()) - 64)); "y", (tmpl.height() - textImage.height()) - 64));
VImage watermark = VImage watermark =
composited.resize((double)width / (double)composited.width()); composited.resize((double)width / (double)composited.width());
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage frame = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, VImage frame = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("expand", true)); VImage::option()->set("expand", true));
img.push_back(frame); img.push_back(frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight + watermark.height()); final.set(VIPS_META_PAGE_HEIGHT, pageHeight + watermark.height());
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight + watermark.height();
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Reddit(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Reddit(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,54 +1,59 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Resize(string type, string *outType, char *BufferData, ArgumentMap Resize(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
bool stretch = GetArgumentWithFallback<bool>(Arguments, "stretch", false); bool stretch = GetArgumentWithFallback<bool>(Arguments, "stretch", false);
bool wide = GetArgumentWithFallback<bool>(Arguments, "wide", false); bool wide = GetArgumentWithFallback<bool>(Arguments, "wide", false);
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
VImage out; VImage out;
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int finalHeight; int finalHeight;
if (stretch) { if (stretch) {
out = out =
in.resize(512.0 / (double)width, in.resize(512.0 / (double)width,
VImage::option()->set("vscale", 512.0 / (double)pageHeight)); VImage::option()->set("vscale", 512.0 / (double)pageHeight));
finalHeight = 512; finalHeight = 512;
} else if (wide) { } else if (wide) {
out = in.resize(9.5, VImage::option()->set("vscale", 0.5)); out = in.resize(9.5, VImage::option()->set("vscale", 0.5));
finalHeight = pageHeight / 2; finalHeight = pageHeight / 2;
} else { } else {
// Pain. Pain. Pain. Pain. Pain. // Pain. Pain. Pain. Pain. Pain.
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage resized = img_frame.resize(0.1).resize( VImage resized = img_frame.resize(0.1).resize(
10, VImage::option()->set("kernel", VIPS_KERNEL_NEAREST)); 10, VImage::option()->set("kernel", VIPS_KERNEL_NEAREST));
img.push_back(resized); img.push_back(resized);
finalHeight = resized.height(); finalHeight = resized.height();
} }
out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); out = VImage::arrayjoin(img, VImage::option()->set("across", 1));
} }
out.set(VIPS_META_PAGE_HEIGHT, finalHeight); out.set(VIPS_META_PAGE_HEIGHT, finalHeight);
void *buf; void *buf;
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = finalHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Resize(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Resize(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,59 +1,64 @@
#include <algorithm> #include <algorithm>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Reverse(string type, string *outType, char *BufferData, ArgumentMap Reverse(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
bool soos = GetArgumentWithFallback<bool>(Arguments, "soos", false); bool soos = GetArgumentWithFallback<bool>(Arguments, "soos", false);
VOption *options = VOption *options =
VImage::option()->set("access", "sequential")->set("n", -1); VImage::option()->set("access", "sequential")->set("n", -1);
VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", options) VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
vector<VImage> split; vector<VImage> split;
// todo: find a better way of getting individual frames (or at least getting // todo: find a better way of getting individual frames (or at least getting
// the frames in reverse order) // the frames in reverse order)
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight);
split.push_back(img_frame); split.push_back(img_frame);
} }
vector<int> delays = in.get_array_int("delay"); vector<int> delays = in.get_array_int("delay");
if (soos) { if (soos) {
vector<VImage> copy = split; vector<VImage> copy = split;
vector<int> copy2 = delays; vector<int> copy2 = delays;
reverse(copy.begin(), copy.end()); reverse(copy.begin(), copy.end());
reverse(copy2.begin(), copy2.end()); reverse(copy2.begin(), copy2.end());
copy.pop_back(); copy.pop_back();
copy2.pop_back(); copy2.pop_back();
copy.erase(copy.begin()); copy.erase(copy.begin());
copy2.erase(copy2.begin()); copy2.erase(copy2.begin());
split.insert(split.end(), copy.begin(), copy.end()); split.insert(split.end(), copy.begin(), copy.end());
delays.insert(delays.end(), copy2.begin(), copy2.end()); delays.insert(delays.end(), copy2.begin(), copy2.end());
} else { } else {
reverse(split.begin(), split.end()); reverse(split.begin(), split.end());
reverse(delays.begin(), delays.end()); reverse(delays.begin(), delays.end());
} }
VImage final = VImage::arrayjoin(split, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(split, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
final.set("delay", delays); final.set("delay", delays);
void *buf; void *buf;
final.write_to_buffer(".gif", &buf, DataSize, final.write_to_buffer(".gif", &buf, DataSize,
VImage::option()->set("dither", 0)); VImage::option()->set("dither", 0));
*outType = "gif"; *outType = "gif";
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Reverse(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Reverse(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,55 +1,61 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Scott(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Scott(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
string assetPath = basePath + "assets/images/scott.png"; string assetPath = basePath + "assets/images/scott.png";
VImage bg = VImage::new_from_file(assetPath.c_str()); VImage bg = VImage::new_from_file(assetPath.c_str());
string distortPath = basePath + "assets/images/scottmap.png"; string distortPath = basePath + "assets/images/scottmap.png";
VImage distort = VImage::new_from_file(distortPath.c_str()); VImage distort = VImage::new_from_file(distortPath.c_str());
VImage distortImage = VImage distortImage =
((distort[1] / 255) * 414).bandjoin((distort[0] / 255) * 233); ((distort[1] / 255) * 414).bandjoin((distort[0] / 255) * 233);
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage resized = img_frame.resize( VImage resized = img_frame.resize(
415 / (double)width, 415 / (double)width,
VImage::option()->set("vscale", 234 / (double)pageHeight)); VImage::option()->set("vscale", 234 / (double)pageHeight));
VImage mapped = resized.mapim(distortImage) VImage mapped = resized.mapim(distortImage)
.extract_band(0, VImage::option()->set("n", 3)) .extract_band(0, VImage::option()->set("n", 3))
.bandjoin(distort[2]); .bandjoin(distort[2]);
VImage offset = mapped.embed(127, 181, 864, 481); VImage offset = mapped.embed(127, 181, 864, 481);
VImage composited = bg.composite2(offset, VIPS_BLEND_MODE_OVER); VImage composited = bg.composite2(offset, VIPS_BLEND_MODE_OVER);
img.push_back(composited); img.push_back(composited);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, 481); final.set(VIPS_META_PAGE_HEIGHT, 481);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" ? VImage::option()->set("dither", 1) : 0); *outType == "gif" ? VImage::option()->set("dither", 1) : 0);
return (char *)buf;
ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Scott(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Scott(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,72 +1,77 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Snapchat(string type, string *outType, char *BufferData, ArgumentMap Snapchat(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string caption = GetArgument<string>(Arguments, "caption"); string caption = GetArgument<string>(Arguments, "caption");
float pos = GetArgumentWithFallback<float>(Arguments, "pos", 0.5); float pos = GetArgumentWithFallback<float>(Arguments, "pos", 0.5);
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int size = width / 20; int size = width / 20;
int textWidth = width - ((width / 25) * 2); int textWidth = width - ((width / 25) * 2);
string font_string = "Helvetica Neue, Twemoji Color Font " + to_string(size); string font_string = "Helvetica Neue, Twemoji Color Font " + to_string(size);
VImage textIn = VImage::text( VImage textIn = VImage::text(
".", VImage::option()->set( ".", VImage::option()->set(
"fontfile", (basePath + "assets/fonts/caption2.ttf").c_str())); "fontfile", (basePath + "assets/fonts/caption2.ttf").c_str()));
textIn = VImage::text( textIn = VImage::text(
("<span foreground=\"white\" background=\"#000000B2\">" + caption + ("<span foreground=\"white\" background=\"#000000B2\">" + caption +
"</span>") "</span>")
.c_str(), .c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", textWidth)); ->set("width", textWidth));
int bgHeight = textIn.height() + (width / 25); int bgHeight = textIn.height() + (width / 25);
textIn = ((textIn == (vector<double>){0, 0, 0, 0}).bandand()) textIn = ((textIn == (vector<double>){0, 0, 0, 0}).bandand())
.ifthenelse({0, 0, 0, 178}, textIn) .ifthenelse({0, 0, 0, 178}, textIn)
.embed((width / 2) - (textIn.width() / 2), .embed((width / 2) - (textIn.width() / 2),
(bgHeight / 2) - (textIn.height() / 2), width, bgHeight, (bgHeight / 2) - (textIn.height() / 2), width, bgHeight,
VImage::option() VImage::option()
->set("extend", "background") ->set("extend", "background")
->set("background", (vector<double>){0, 0, 0, 178})); ->set("background", (vector<double>){0, 0, 0, 178}));
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
img_frame = img_frame.composite2( img_frame = img_frame.composite2(
textIn, VIPS_BLEND_MODE_OVER, textIn, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", 0)->set("y", pageHeight * pos)); VImage::option()->set("x", 0)->set("y", pageHeight * pos));
img.push_back(img_frame); img.push_back(img_frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Snapchat(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Snapchat(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,35 +1,40 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Sonic(string type, string *outType, ArgumentMap Arguments, ArgumentMap Sonic(string type, string *outType, ArgumentMap Arguments,
size_t *DataSize) { size_t *DataSize) {
string text = GetArgument<string>(Arguments, "text"); string text = GetArgument<string>(Arguments, "text");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
string assetPath = basePath + "assets/images/sonic.jpg"; string assetPath = basePath + "assets/images/sonic.jpg";
VImage bg = VImage::new_from_file(assetPath.c_str()); VImage bg = VImage::new_from_file(assetPath.c_str());
VImage textImage = VImage textImage =
VImage::text( VImage::text(
("<span foreground=\"white\">" + text + "</span>").c_str(), ("<span foreground=\"white\">" + text + "</span>").c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", "Verdana, Twemoji Color Font") ->set("font", "Verdana, Twemoji Color Font")
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", 542) ->set("width", 542)
->set("height", 390)) ->set("height", 390))
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, 542, 390); .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 542, 390);
VImage out = bg.composite2(textImage, VIPS_BLEND_MODE_OVER, VImage out = bg.composite2(textImage, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", 391)->set("y", 84)); VImage::option()->set("x", 391)->set("y", 84));
void *buf; void *buf;
out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = out.width();
output["height"] = vips_image_get_page_height(out.get_image());
return output;
} }

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char *Sonic(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); ArgumentMap Sonic(string type, string *outType, ArgumentMap Arguments, size_t *DataSize);

View file

@ -1,102 +1,109 @@
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
void *memset16(void *m, uint16_t val, size_t count) { void *memset16(void *m, uint16_t val, size_t count) {
uint16_t *buf = (uint16_t *)m; uint16_t *buf = (uint16_t *)m;
while (count--) *buf++ = val; while (count--) *buf++ = val;
return m; return m;
} }
char *vipsRemove(char *data, size_t length, size_t *DataSize, int speed) { ArgumentMap vipsHandle(char *data, size_t length, size_t *DataSize, int speed, bool removeFrames) {
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage::new_from_buffer(data, length, "", options->set("n", -1)) VImage in = VImage::new_from_buffer(data, length, "", options->set("n", -1))
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
vector<VImage> img; VImage out;
for (int i = 0; i < nPages; i += speed) { if (removeFrames) {
VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); vector<VImage> img;
img.push_back(img_frame); for (int i = 0; i < nPages; i += speed) {
} VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight);
VImage out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); img.push_back(img_frame);
out.set(VIPS_META_PAGE_HEIGHT, pageHeight); }
out = VImage::arrayjoin(img, VImage::option()->set("across", 1));
void *buf; out.set(VIPS_META_PAGE_HEIGHT, pageHeight);
out.write_to_buffer(".gif", &buf, DataSize); } else {
out = in;
return (char *)buf; }
}
void *buf;
char *Speed([[maybe_unused]] string type, string *outType, char *BufferData, out.write_to_buffer(".gif", &buf, DataSize);
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
bool slow = GetArgumentWithFallback<bool>(Arguments, "slow", false); ArgumentMap output;
int speed = GetArgumentWithFallback<int>(Arguments, "speed", 2); output["buf"] = (char *)buf;
output["width"] = width;
char *fileData = (char *)malloc(BufferLength); output["height"] = pageHeight;
memcpy(fileData, BufferData, BufferLength);
return output;
char *match = (char *)"\x00\x21\xF9\x04"; }
vector<uint16_t> old_delays; ArgumentMap Speed([[maybe_unused]] string type, string *outType, char *BufferData,
bool removeFrames = false; size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
char *lastPos; bool slow = GetArgumentWithFallback<bool>(Arguments, "slow", false);
int speed = GetArgumentWithFallback<int>(Arguments, "speed", 2);
// int amount = 0;
char *fileData = (char *)malloc(BufferLength);
lastPos = (char *)memchr(fileData, '\x00', BufferLength); memcpy(fileData, BufferData, BufferLength);
while (lastPos != NULL) {
if (memcmp(lastPos, match, 4) != 0) { char *match = (char *)"\x00\x21\xF9\x04";
lastPos = (char *)memchr(lastPos + 1, '\x00',
(BufferLength - (lastPos - fileData)) - 1); vector<uint16_t> old_delays;
continue; bool removeFrames = false;
} char *lastPos;
//++amount;
uint16_t old_delay; // int amount = 0;
memcpy(&old_delay, lastPos + 5, 2);
old_delays.push_back(old_delay); lastPos = (char *)memchr(fileData, '\x00', BufferLength);
lastPos = (char *)memchr(lastPos + 1, '\x00', while (lastPos != NULL) {
(BufferLength - (lastPos - fileData)) - 1); if (memcmp(lastPos, match, 4) != 0) {
} lastPos = (char *)memchr(lastPos + 1, '\x00',
(BufferLength - (lastPos - fileData)) - 1);
int currentFrame = 0; continue;
lastPos = (char *)memchr(fileData, '\x00', BufferLength); }
while (lastPos != NULL) { //++amount;
if (memcmp(lastPos, match, 4) != 0) { uint16_t old_delay;
lastPos = (char *)memchr(lastPos + 1, '\x00', memcpy(&old_delay, lastPos + 5, 2);
(BufferLength - (lastPos - fileData)) - 1); old_delays.push_back(old_delay);
continue; lastPos = (char *)memchr(lastPos + 1, '\x00',
} (BufferLength - (lastPos - fileData)) - 1);
uint16_t new_delay = slow ? old_delays[currentFrame] * speed }
: old_delays[currentFrame] / speed;
if (!slow && new_delay <= 1) { int currentFrame = 0;
removeFrames = true; lastPos = (char *)memchr(fileData, '\x00', BufferLength);
break; while (lastPos != NULL) {
} if (memcmp(lastPos, match, 4) != 0) {
lastPos = (char *)memchr(lastPos + 1, '\x00',
memset16(lastPos + 5, new_delay, 1); (BufferLength - (lastPos - fileData)) - 1);
continue;
lastPos = (char *)memchr(lastPos + 1, '\x00', }
(BufferLength - (lastPos - fileData)) - 1); uint16_t new_delay = slow ? old_delays[currentFrame] * speed
++currentFrame; : old_delays[currentFrame] / speed;
} if (!slow && new_delay <= 1) {
removeFrames = true;
if (removeFrames) { break;
fileData = vipsRemove(BufferData, BufferLength, DataSize, speed); }
} else {
*DataSize = BufferLength; memset16(lastPos + 5, new_delay, 1);
}
lastPos = (char *)memchr(lastPos + 1, '\x00',
return fileData; (BufferLength - (lastPos - fileData)) - 1);
++currentFrame;
}
// TODO: this is cursed, fix it later
ArgumentMap output = vipsHandle(BufferData, BufferLength, DataSize, speed, removeFrames);
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Speed(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Speed(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,72 +1,78 @@
#include <Magick++.h> #include <Magick++.h>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace Magick; using namespace Magick;
char *Spin(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Spin(string type, string *outType, char *BufferData, size_t BufferLength,
[[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) {
int delay = GetArgumentWithFallback<int>(Arguments, "delay", 0); int delay = GetArgumentWithFallback<int>(Arguments, "delay", 0);
Blob blob; Blob blob;
list<Image> frames; list<Image> frames;
list<Image> coalesced; list<Image> coalesced;
list<Image> mid; list<Image> mid;
try { try {
readImages(&frames, Blob(BufferData, BufferLength)); readImages(&frames, Blob(BufferData, BufferLength));
} catch (Magick::WarningCoder &warning) { } catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl; cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) { } catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl; cerr << "Warning: " << warning.what() << endl;
} }
coalesceImages(&coalesced, frames.begin(), frames.end()); coalesceImages(&coalesced, frames.begin(), frames.end());
if (type != "gif") { if (type != "gif") {
list<Image>::iterator it = coalesced.begin(); list<Image>::iterator it = coalesced.begin();
for (int i = 0; i < 29; ++i) { for (int i = 0; i < 29; ++i) {
coalesced.push_back(*it); coalesced.push_back(*it);
} }
} }
int i = 0; int i = 0;
for (Image &image : coalesced) { for (Image &image : coalesced) {
image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod); image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod);
image.scale(Geometry("256x256")); image.scale(Geometry("256x256"));
image.alphaChannel(Magick::SetAlphaChannel); image.alphaChannel(Magick::SetAlphaChannel);
double rotation[1] = {(double)360 * i / coalesced.size()}; double rotation[1] = {(double)360 * i / coalesced.size()};
image.distort(Magick::ScaleRotateTranslateDistortion, 1, rotation); image.distort(Magick::ScaleRotateTranslateDistortion, 1, rotation);
image.magick("GIF"); image.magick("GIF");
mid.push_back(image); mid.push_back(image);
i++; i++;
} }
for_each(mid.begin(), mid.end(), for_each(mid.begin(), mid.end(),
gifDisposeMethodImage(Magick::BackgroundDispose)); gifDisposeMethodImage(Magick::BackgroundDispose));
optimizeTransparency(mid.begin(), mid.end()); optimizeTransparency(mid.begin(), mid.end());
if (delay != 0) { if (delay != 0) {
for_each(mid.begin(), mid.end(), animationDelayImage(delay)); for_each(mid.begin(), mid.end(), animationDelayImage(delay));
} else if (type != "gif") { } else if (type != "gif") {
for_each(mid.begin(), mid.end(), animationDelayImage(5)); for_each(mid.begin(), mid.end(), animationDelayImage(5));
} }
for (Image &image : mid) { for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod); image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize(); image.quantize();
} }
writeImages(mid.begin(), mid.end(), &blob); writeImages(mid.begin(), mid.end(), &blob);
*outType = "gif"; *outType = "gif";
*DataSize = blob.length(); *DataSize = blob.length();
char *data = (char *)malloc(*DataSize); char *data = (char *)malloc(*DataSize);
memcpy(data, blob.data(), *DataSize); memcpy(data, blob.data(), *DataSize);
return data;
} ArgumentMap output;
output["buf"] = data;
output["width"] = (int)mid.front().columns();
output["height"] = (int)mid.front().rows();
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Spin(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Spin(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,52 +1,57 @@
#include <math.h> #include <math.h>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Squish(string type, string *outType, char *BufferData, ArgumentMap Squish(string type, string *outType, char *BufferData,
size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments,
size_t *DataSize) { size_t *DataSize) {
VOption *options = VImage::option(); VOption *options = VImage::option();
VImage in = VImage in =
VImage::new_from_buffer( VImage::new_from_buffer(
BufferData, BufferLength, "", BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1)->set("access", "sequential") type == "gif" ? options->set("n", -1)->set("access", "sequential")
: options) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30;
double mult = (2 * M_PI) / nPages; double mult = (2 * M_PI) / nPages;
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
double newWidth = (sin(i * mult) / 4) + 0.75; double newWidth = (sin(i * mult) / 4) + 0.75;
double newHeight = (cos(i * mult) / 4) + 0.75; double newHeight = (cos(i * mult) / 4) + 0.75;
VImage resized = VImage resized =
img_frame.resize(newWidth, VImage::option()->set("vscale", newHeight)) img_frame.resize(newWidth, VImage::option()->set("vscale", newHeight))
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, pageHeight); .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, pageHeight);
img.push_back(resized); img.push_back(resized);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
if (type != "gif") { if (type != "gif") {
vector<int> delay(30, 50); vector<int> delay(30, 50);
final.set("delay", delay); final.set("delay", delay);
} }
void *buf; void *buf;
final.write_to_buffer(".gif", &buf, DataSize); final.write_to_buffer(".gif", &buf, DataSize);
*outType = "gif"; *outType = "gif";
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(final.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Squish(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Squish(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,76 +1,81 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Swirl(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Swirl(string type, string *outType, char *BufferData, size_t BufferLength,
[[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) {
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int width = in.width(); int width = in.width();
double newWidth = width * 3; double newWidth = width * 3;
double newHeight = pageHeight * 3; double newHeight = pageHeight * 3;
vector<double> divSize = {newWidth / 2, newHeight / 2}; vector<double> divSize = {newWidth / 2, newHeight / 2};
VImage index = VImage::xyz(newWidth, newHeight); VImage index = VImage::xyz(newWidth, newHeight);
VImage center = index - divSize; VImage center = index - divSize;
VImage polar = center VImage polar = center
.copy(VImage::option() .copy(VImage::option()
->set("format", VIPS_FORMAT_COMPLEX) ->set("format", VIPS_FORMAT_COMPLEX)
->set("bands", 1)) ->set("bands", 1))
.polar() .polar()
.copy(VImage::option() .copy(VImage::option()
->set("format", VIPS_FORMAT_FLOAT) ->set("format", VIPS_FORMAT_FLOAT)
->set("bands", 2)); ->set("bands", 2));
int size = min(width, pageHeight) / 2; int size = min(width, pageHeight) / 2;
VImage test = (1 - polar.extract_band(0) / size); VImage test = (1 - polar.extract_band(0) / size);
VImage degrees = test.cast(VIPS_FORMAT_FLOAT).pow(2); VImage degrees = test.cast(VIPS_FORMAT_FLOAT).pow(2);
VImage angle = polar.extract_band(1) + degrees * 180; VImage angle = polar.extract_band(1) + degrees * 180;
VImage distortion = polar.extract_band(0) VImage distortion = polar.extract_band(0)
.bandjoin(angle) .bandjoin(angle)
.copy(VImage::option() .copy(VImage::option()
->set("format", VIPS_FORMAT_COMPLEX) ->set("format", VIPS_FORMAT_COMPLEX)
->set("bands", 1)) ->set("bands", 1))
.rect() .rect()
.copy(VImage::option() .copy(VImage::option()
->set("format", VIPS_FORMAT_FLOAT) ->set("format", VIPS_FORMAT_FLOAT)
->set("bands", 2)) + ->set("bands", 2)) +
divSize; divSize;
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage distort = VImage distort =
img_frame img_frame
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, newWidth, newHeight, .gravity(VIPS_COMPASS_DIRECTION_CENTRE, newWidth, newHeight,
VImage::option()->set("extend", VIPS_EXTEND_COPY)) VImage::option()->set("extend", VIPS_EXTEND_COPY))
.mapim(distortion, .mapim(distortion,
VImage::option()->set( VImage::option()->set(
"interpolate", VInterpolate::new_from_name("bicubic"))); "interpolate", VInterpolate::new_from_name("bicubic")));
VImage frame = distort.crop(width, pageHeight, width, pageHeight); VImage frame = distort.crop(width, pageHeight, width, pageHeight);
img.push_back(frame); img.push_back(frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
void *buf; void *buf;
final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(final.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Swirl(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Swirl(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,65 +1,71 @@
#include <Magick++.h> #include <Magick++.h>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace Magick; using namespace Magick;
char *Tile(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Tile(string type, string *outType, char *BufferData, size_t BufferLength,
[[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) {
Blob blob; Blob blob;
list<Image> frames; list<Image> frames;
list<Image> coalesced; list<Image> coalesced;
list<Image> mid; list<Image> mid;
try { try {
readImages(&frames, Blob(BufferData, BufferLength)); readImages(&frames, Blob(BufferData, BufferLength));
} catch (Magick::WarningCoder &warning) { } catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl; cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) { } catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl; cerr << "Warning: " << warning.what() << endl;
} }
coalesceImages(&coalesced, frames.begin(), frames.end()); coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) { for (Image &image : coalesced) {
list<Image> duplicated; list<Image> duplicated;
Image appended; Image appended;
list<Image> montage; list<Image> montage;
Image frame; Image frame;
image.magick(*outType); image.magick(*outType);
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
duplicated.push_back(image); duplicated.push_back(image);
} }
appendImages(&appended, duplicated.begin(), duplicated.end()); appendImages(&appended, duplicated.begin(), duplicated.end());
appended.repage(); appended.repage();
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
montage.push_back(appended); montage.push_back(appended);
} }
appendImages(&frame, montage.begin(), montage.end(), true); appendImages(&frame, montage.begin(), montage.end(), true);
frame.repage(); frame.repage();
frame.scale(Geometry("800x800>")); frame.scale(Geometry("800x800>"));
frame.animationDelay(image.animationDelay()); frame.animationDelay(image.animationDelay());
mid.push_back(frame); mid.push_back(frame);
} }
optimizeTransparency(mid.begin(), mid.end()); optimizeTransparency(mid.begin(), mid.end());
if (*outType == "gif") { if (*outType == "gif") {
for (Image &image : mid) { for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod); image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize(); image.quantize();
} }
} }
writeImages(mid.begin(), mid.end(), &blob); writeImages(mid.begin(), mid.end(), &blob);
*DataSize = blob.length(); *DataSize = blob.length();
char *data = (char *)malloc(*DataSize); char *data = (char *)malloc(*DataSize);
memcpy(data, blob.data(), *DataSize); memcpy(data, blob.data(), *DataSize);
return data;
ArgumentMap output;
output["buf"] = data;
output["width"] = (int)mid.front().columns();
output["height"] = (int)mid.front().rows();
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Tile(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Tile(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,28 +1,38 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *ToGif(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap ToGif(string type, string *outType, char *BufferData, size_t BufferLength,
[[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) {
if (type == "gif") { if (type == "gif") {
*DataSize = BufferLength; *DataSize = BufferLength;
char *data = (char *)malloc(BufferLength); char *data = (char *)malloc(BufferLength);
memcpy(data, BufferData, BufferLength); memcpy(data, BufferData, BufferLength);
return data;
} else { ArgumentMap output;
VOption *options = VImage::option()->set("access", "sequential"); output["buf"] = data;
VImage in = VImage::new_from_buffer( return output;
BufferData, BufferLength, "",
type == "webp" ? options->set("n", -1) : options); } else {
VOption *options = VImage::option()->set("access", "sequential");
void *buf;
in.write_to_buffer(".gif", &buf, DataSize); VImage in = VImage::new_from_buffer(
*outType = "gif"; BufferData, BufferLength, "",
type == "webp" ? options->set("n", -1) : options);
return (char *)buf;
} void *buf;
in.write_to_buffer(".gif", &buf, DataSize);
*outType = "gif";
ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = in.width();
output["height"] = vips_image_get_page_height(in.get_image());
return output;
}
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* ToGif(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap ToGif(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,101 +1,106 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Uncanny(string type, string *outType, char *BufferData, ArgumentMap Uncanny(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string caption = GetArgument<string>(Arguments, "caption"); string caption = GetArgument<string>(Arguments, "caption");
string caption2 = GetArgument<string>(Arguments, "caption2"); string caption2 = GetArgument<string>(Arguments, "caption2");
string font = GetArgument<string>(Arguments, "font"); string font = GetArgument<string>(Arguments, "font");
string path = GetArgument<string>(Arguments, "path"); string path = GetArgument<string>(Arguments, "path");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB) .colourspace(VIPS_INTERPRETATION_sRGB)
.extract_band(0, VImage::option()->set("n", 3)); .extract_band(0, VImage::option()->set("n", 3));
VImage base = VImage::black(1280, 720, VImage::option()->set("bands", 3)); VImage base = VImage::black(1280, 720, VImage::option()->set("bands", 3));
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + string font_string = (font == "roboto" ? "Roboto Condensed" : font) +
", Twemoji Color Font " + ", Twemoji Color Font " +
(font != "impact" ? "bold" : "normal") + " 72"; (font != "impact" ? "bold" : "normal") + " 72";
string captionText = string captionText =
"<span background=\"black\" foreground=\"white\">" + caption + "</span>"; "<span background=\"black\" foreground=\"white\">" + caption + "</span>";
string caption2Text = string caption2Text =
"<span background=\"black\" foreground=\"red\">" + caption2 + "</span>"; "<span background=\"black\" foreground=\"red\">" + caption2 + "</span>";
auto findResult = fontPaths.find(font); auto findResult = fontPaths.find(font);
if (findResult != fontPaths.end()) { if (findResult != fontPaths.end()) {
VImage::text(".", VImage::option()->set( VImage::text(".", VImage::option()->set(
"fontfile", (basePath + findResult->second).c_str())); "fontfile", (basePath + findResult->second).c_str()));
} }
VImage text = VImage::text( VImage text = VImage::text(
captionText.c_str(), captionText.c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", 588) ->set("width", 588)
->set("height", 90)); ->set("height", 90));
VImage captionImage = VImage captionImage =
text.extract_band(0, VImage::option()->set("n", 3)) text.extract_band(0, VImage::option()->set("n", 3))
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40,
VImage::option()->set("extend", "black")); VImage::option()->set("extend", "black"));
VImage text2 = VImage::text( VImage text2 = VImage::text(
caption2Text.c_str(), caption2Text.c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", 588) ->set("width", 588)
->set("height", 90)); ->set("height", 90));
VImage caption2Image = VImage caption2Image =
text2.extract_band(0, VImage::option()->set("n", 3)) text2.extract_band(0, VImage::option()->set("n", 3))
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40,
VImage::option()->set("extend", "black")); VImage::option()->set("extend", "black"));
base = base.insert(captionImage, 0, 0).insert(caption2Image, 640, 0); base = base.insert(captionImage, 0, 0).insert(caption2Image, 640, 0);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
VImage uncanny = VImage::new_from_file((basePath + path).c_str()); VImage uncanny = VImage::new_from_file((basePath + path).c_str());
base = base.insert(uncanny, 0, 130); base = base.insert(uncanny, 0, 130);
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage resized = img_frame.resize(690.0 / (double)width); VImage resized = img_frame.resize(690.0 / (double)width);
if (resized.height() > 590) { if (resized.height() > 590) {
double vscale = 590.0 / (double)resized.height(); double vscale = 590.0 / (double)resized.height();
resized = resized.resize(vscale, VImage::option()->set("vscale", vscale)); resized = resized.resize(vscale, VImage::option()->set("vscale", vscale));
} }
VImage composited = base.insert(resized, 935 - (resized.width() / 2), VImage composited = base.insert(resized, 935 - (resized.width() / 2),
425 - (resized.height() / 2)); 425 - (resized.height() / 2));
img.push_back(composited); img.push_back(composited);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, 720); final.set(VIPS_META_PAGE_HEIGHT, 720);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" ? VImage::option()->set("reoptimise", 1) : 0); *outType == "gif" ? VImage::option()->set("reoptimise", 1) : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(final.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Uncanny(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Uncanny(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,54 +1,59 @@
#include <map> #include <map>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Uncaption(string type, string *outType, char *BufferData, ArgumentMap Uncaption(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
float tolerance = GetArgumentWithFallback<float>(Arguments, "tolerance", 0.5); float tolerance = GetArgumentWithFallback<float>(Arguments, "tolerance", 0.5);
VOption *options = VImage::option(); VOption *options = VImage::option();
VImage in = VImage in =
VImage::new_from_buffer( VImage::new_from_buffer(
BufferData, BufferLength, "", BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1)->set("access", "sequential") type == "gif" ? options->set("n", -1)->set("access", "sequential")
: options) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
VImage first = VImage first =
in.crop(0, 0, 3, pageHeight).colourspace(VIPS_INTERPRETATION_B_W) > in.crop(0, 0, 3, pageHeight).colourspace(VIPS_INTERPRETATION_B_W) >
(255 * tolerance); (255 * tolerance);
int top, captionWidth, captionHeight; int top, captionWidth, captionHeight;
first.find_trim(&top, &captionWidth, &captionHeight); first.find_trim(&top, &captionWidth, &captionHeight);
vector<VImage> img; vector<VImage> img;
int newHeight = pageHeight - top; int newHeight = pageHeight - top;
if (top == pageHeight) { if (top == pageHeight) {
newHeight = pageHeight; newHeight = pageHeight;
top = 0; top = 0;
} }
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = in.crop(0, (i * pageHeight) + top, width, newHeight); VImage img_frame = in.crop(0, (i * pageHeight) + top, width, newHeight);
img.push_back(img_frame); img.push_back(img_frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, newHeight); final.set(VIPS_META_PAGE_HEIGHT, newHeight);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(final.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Uncaption(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Uncaption(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,58 +1,64 @@
#include <Magick++.h> #include <Magick++.h>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace Magick; using namespace Magick;
char *Wall(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Wall(string type, string *outType, char *BufferData, size_t BufferLength,
[[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) {
Blob blob; Blob blob;
list<Image> frames; list<Image> frames;
list<Image> coalesced; list<Image> coalesced;
list<Image> mid; list<Image> mid;
try { try {
readImages(&frames, Blob(BufferData, BufferLength)); readImages(&frames, Blob(BufferData, BufferLength));
} catch (Magick::WarningCoder &warning) { } catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl; cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) { } catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl; cerr << "Warning: " << warning.what() << endl;
} }
coalesceImages(&coalesced, frames.begin(), frames.end()); coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) { for (Image &image : coalesced) {
image.resize(Geometry("128x128")); image.resize(Geometry("128x128"));
image.virtualPixelMethod(Magick::TileVirtualPixelMethod); image.virtualPixelMethod(Magick::TileVirtualPixelMethod);
image.matteColor("none"); image.matteColor("none");
image.backgroundColor("none"); image.backgroundColor("none");
image.scale(Geometry("512x512")); image.scale(Geometry("512x512"));
double arguments[16] = {0, 0, 57, 42, 0, 128, 63, 130, double arguments[16] = {0, 0, 57, 42, 0, 128, 63, 130,
128, 0, 140, 60, 128, 128, 140, 140}; 128, 0, 140, 60, 128, 128, 140, 140};
image.distort(Magick::PerspectiveDistortion, 16, arguments); image.distort(Magick::PerspectiveDistortion, 16, arguments);
image.scale(Geometry("800x800>")); image.scale(Geometry("800x800>"));
image.magick(*outType); image.magick(*outType);
mid.push_back(image); mid.push_back(image);
} }
optimizeTransparency(mid.begin(), mid.end()); optimizeTransparency(mid.begin(), mid.end());
if (*outType == "gif") { if (*outType == "gif") {
for (Image &image : mid) { for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod); image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize(); image.quantize();
} }
} }
writeImages(mid.begin(), mid.end(), &blob); writeImages(mid.begin(), mid.end(), &blob);
*DataSize = blob.length(); *DataSize = blob.length();
char *data = (char *)malloc(*DataSize); char *data = (char *)malloc(*DataSize);
memcpy(data, blob.data(), *DataSize); memcpy(data, blob.data(), *DataSize);
return data;
ArgumentMap output;
output["buf"] = data;
output["width"] = (int)mid.front().columns();
output["height"] = (int)mid.front().rows();
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Wall(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Wall(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,154 +1,159 @@
#include <map> #include <map>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Watermark(string type, string *outType, char *BufferData, ArgumentMap Watermark(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string water = GetArgument<string>(Arguments, "water"); string water = GetArgument<string>(Arguments, "water");
int gravity = GetArgument<int>(Arguments, "gravity"); int gravity = GetArgument<int>(Arguments, "gravity");
bool resize = GetArgumentWithFallback<bool>(Arguments, "resize", false); bool resize = GetArgumentWithFallback<bool>(Arguments, "resize", false);
float yscale = GetArgumentWithFallback<float>(Arguments, "yscale", false); float yscale = GetArgumentWithFallback<float>(Arguments, "yscale", false);
bool append = GetArgumentWithFallback<bool>(Arguments, "append", false); bool append = GetArgumentWithFallback<bool>(Arguments, "append", false);
bool alpha = GetArgumentWithFallback<bool>(Arguments, "alpha", false); bool alpha = GetArgumentWithFallback<bool>(Arguments, "alpha", false);
bool flip = GetArgumentWithFallback<bool>(Arguments, "flip", false); bool flip = GetArgumentWithFallback<bool>(Arguments, "flip", false);
bool mc = MAP_HAS(Arguments, "mc"); bool mc = MAP_HAS(Arguments, "mc");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
string merged = basePath + water; string merged = basePath + water;
VImage watermark = VImage::new_from_file(merged.c_str()); VImage watermark = VImage::new_from_file(merged.c_str());
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
if (flip) { if (flip) {
watermark = watermark.flip(VIPS_DIRECTION_HORIZONTAL); watermark = watermark.flip(VIPS_DIRECTION_HORIZONTAL);
} }
if (resize && append) { if (resize && append) {
watermark = watermark.resize((double)width / (double)watermark.width()); watermark = watermark.resize((double)width / (double)watermark.width());
} else if (resize && yscale) { } else if (resize && yscale) {
watermark = watermark.resize( watermark = watermark.resize(
(double)width / (double)watermark.width(), (double)width / (double)watermark.width(),
VImage::option()->set("vscale", (double)(pageHeight * yscale) / VImage::option()->set("vscale", (double)(pageHeight * yscale) /
(double)watermark.height())); (double)watermark.height()));
} else if (resize) { } else if (resize) {
watermark = watermark =
watermark.resize((double)pageHeight / (double)watermark.height()); watermark.resize((double)pageHeight / (double)watermark.height());
} }
int x = 0, y = 0; int x = 0, y = 0;
switch (gravity) { switch (gravity) {
case 1: case 1:
break; break;
case 2: case 2:
x = (width / 2) - (watermark.width() / 2); x = (width / 2) - (watermark.width() / 2);
break; break;
case 3: case 3:
x = width - watermark.width(); x = width - watermark.width();
break; break;
case 5: case 5:
x = (width / 2) - (watermark.width() / 2); x = (width / 2) - (watermark.width() / 2);
y = (pageHeight / 2) - (watermark.height() / 2); y = (pageHeight / 2) - (watermark.height() / 2);
break; break;
case 6: case 6:
x = width - watermark.width(); x = width - watermark.width();
y = (pageHeight / 2) - (watermark.height() / 2); y = (pageHeight / 2) - (watermark.height() / 2);
break; break;
case 8: case 8:
x = (width / 2) - (watermark.width() / 2); x = (width / 2) - (watermark.width() / 2);
y = pageHeight - watermark.height(); y = pageHeight - watermark.height();
break; break;
case 9: case 9:
x = width - watermark.width(); x = width - watermark.width();
y = pageHeight - watermark.height(); y = pageHeight - watermark.height();
break; break;
} }
vector<VImage> img; vector<VImage> img;
int addedHeight = 0; int addedHeight = 0;
VImage contentAlpha; VImage contentAlpha;
VImage frameAlpha; VImage frameAlpha;
VImage bg; VImage bg;
VImage frame; VImage frame;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
if (append) { if (append) {
VImage appended = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, VImage appended = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("expand", true)); VImage::option()->set("expand", true));
addedHeight = watermark.height(); addedHeight = watermark.height();
img.push_back(appended); img.push_back(appended);
} else if (mc) { } else if (mc) {
VImage padded = VImage padded =
img_frame.embed(0, 0, width, pageHeight + 15, img_frame.embed(0, 0, width, pageHeight + 15,
VImage::option()->set("background", 0xffffff)); VImage::option()->set("background", 0xffffff));
VImage composited = VImage composited =
padded.composite2(watermark, VIPS_BLEND_MODE_OVER, padded.composite2(watermark, VIPS_BLEND_MODE_OVER,
VImage::option() VImage::option()
->set("x", width - 190) ->set("x", width - 190)
->set("y", padded.height() - 22)); ->set("y", padded.height() - 22));
addedHeight = 15; addedHeight = 15;
img.push_back(composited); img.push_back(composited);
} else { } else {
VImage composited; VImage composited;
if (alpha) { if (alpha) {
if (i == 0) { if (i == 0) {
contentAlpha = watermark.extract_band(0).embed( contentAlpha = watermark.extract_band(0).embed(
x, y, width, pageHeight, x, y, width, pageHeight,
VImage::option()->set("extend", "white")); VImage::option()->set("extend", "white"));
frameAlpha = watermark.extract_band(1).embed( frameAlpha = watermark.extract_band(1).embed(
x, y, width, pageHeight, x, y, width, pageHeight,
VImage::option()->set("extend", "black")); VImage::option()->set("extend", "black"));
bg = frameAlpha.new_from_image({0, 0, 0}).copy(VImage::option()->set( bg = frameAlpha.new_from_image({0, 0, 0}).copy(VImage::option()->set(
"interpretation", VIPS_INTERPRETATION_sRGB)); "interpretation", VIPS_INTERPRETATION_sRGB));
frame = bg.bandjoin(frameAlpha); frame = bg.bandjoin(frameAlpha);
if (*outType == "jpg" || *outType == "jpeg") { if (*outType == "jpg" || *outType == "jpeg") {
*outType = "png"; *outType = "png";
} }
} }
VImage content = VImage content =
img_frame.extract_band(0, VImage::option()->set("n", 3)) img_frame.extract_band(0, VImage::option()->set("n", 3))
.bandjoin(contentAlpha & img_frame.extract_band(3)); .bandjoin(contentAlpha & img_frame.extract_band(3));
composited = composited =
content.composite2(frame, VIPS_BLEND_MODE_OVER, content.composite2(frame, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", x)->set("y", y)); VImage::option()->set("x", x)->set("y", y));
} else { } else {
composited = composited =
img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER, img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", x)->set("y", y)); VImage::option()->set("x", x)->set("y", y));
} }
img.push_back(composited); img.push_back(composited);
} }
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight + addedHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight + addedHeight);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
} output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(final.get_image());
return output;
}

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Watermark(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Watermark(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,86 +1,91 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Whisper(string type, string *outType, char *BufferData, ArgumentMap Whisper(string type, string *outType, char *BufferData,
size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) {
string caption = GetArgument<string>(Arguments, "caption"); string caption = GetArgument<string>(Arguments, "caption");
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
int size = width / 6; int size = width / 6;
int dividedWidth = width / 175; int dividedWidth = width / 175;
int rad = 1; int rad = 1;
string font_string = "Upright, Twemoji Color Font " + to_string(size); string font_string = "Upright, Twemoji Color Font " + to_string(size);
VImage mask; VImage mask;
if (dividedWidth >= 1) { if (dividedWidth >= 1) {
mask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128; mask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128;
mask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth, mask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth,
VImage::option()->set("fill", true)); VImage::option()->set("fill", true));
} else { } else {
mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128; mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128;
mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true)); mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true));
} }
VImage textIn = VImage::text( VImage textIn = VImage::text(
".", VImage::option()->set( ".", VImage::option()->set(
"fontfile", (basePath + "assets/fonts/whisper.otf").c_str())); "fontfile", (basePath + "assets/fonts/whisper.otf").c_str()));
textIn = VImage::text( textIn = VImage::text(
("<span foreground=\"white\">" + caption + "</span>").c_str(), ("<span foreground=\"white\">" + caption + "</span>").c_str(),
VImage::option() VImage::option()
->set("rgba", true) ->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE) ->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str()) ->set("font", font_string.c_str())
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
->set("width", width)); ->set("width", width));
textIn = textIn.embed(rad + 10, rad + 10, (textIn.width() + 2 * rad) + 20, textIn = textIn.embed(rad + 10, rad + 10, (textIn.width() + 2 * rad) + 20,
(textIn.height() + 2 * rad) + 20); (textIn.height() + 2 * rad) + 20);
VImage outline = textIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) VImage outline = textIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE)
.gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1));
outline = (outline == (vector<double>){0, 0, 0, 0}); outline = (outline == (vector<double>){0, 0, 0, 0});
VImage invert = outline.extract_band(3).invert(); VImage invert = outline.extract_band(3).invert();
outline = outline =
outline.extract_band(0, VImage::option()->set("n", outline.bands() - 1)) outline.extract_band(0, VImage::option()->set("n", outline.bands() - 1))
.bandjoin(invert); .bandjoin(invert);
VImage textImg = outline.composite2(textIn, VIPS_BLEND_MODE_OVER); VImage textImg = outline.composite2(textIn, VIPS_BLEND_MODE_OVER);
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
img_frame = img_frame.composite2( img_frame = img_frame.composite2(
textImg, VIPS_BLEND_MODE_OVER, textImg, VIPS_BLEND_MODE_OVER,
VImage::option() VImage::option()
->set("x", (width / 2) - (textImg.width() / 2)) ->set("x", (width / 2) - (textImg.width() / 2))
->set("y", (pageHeight / 2) - (textImg.height() / 2))); ->set("y", (pageHeight / 2) - (textImg.height() / 2)));
img.push_back(img_frame); img.push_back(img_frame);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, pageHeight); final.set(VIPS_META_PAGE_HEIGHT, pageHeight);
void *buf; void *buf;
final.write_to_buffer( final.write_to_buffer(
("." + *outType).c_str(), &buf, DataSize, ("." + *outType).c_str(), &buf, DataSize,
*outType == "gif" *outType == "gif"
? VImage::option()->set("dither", 0)->set("reoptimise", 1) ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
: 0); : 0);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = width;
output["height"] = pageHeight;
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Whisper(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Whisper(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -1,47 +1,52 @@
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
using namespace std; using namespace std;
using namespace vips; using namespace vips;
char *Zamn(string type, string *outType, char *BufferData, size_t BufferLength, ArgumentMap Zamn(string type, string *outType, char *BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t *DataSize) { ArgumentMap Arguments, size_t *DataSize) {
string basePath = GetArgument<string>(Arguments, "basePath"); string basePath = GetArgument<string>(Arguments, "basePath");
VOption *options = VImage::option()->set("access", "sequential"); VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage in =
VImage::new_from_buffer(BufferData, BufferLength, "", VImage::new_from_buffer(BufferData, BufferLength, "",
type == "gif" ? options->set("n", -1) : options) type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB); .colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255); if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width(); int width = in.width();
int pageHeight = vips_image_get_page_height(in.get_image()); int pageHeight = vips_image_get_page_height(in.get_image());
int nPages = vips_image_get_n_pages(in.get_image()); int nPages = vips_image_get_n_pages(in.get_image());
string assetPath = basePath + "assets/images/zamn.png"; string assetPath = basePath + "assets/images/zamn.png";
VImage tmpl = VImage::new_from_file(assetPath.c_str()); VImage tmpl = VImage::new_from_file(assetPath.c_str());
vector<VImage> img; vector<VImage> img;
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
VImage img_frame = VImage img_frame =
type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in;
VImage composited = tmpl.insert( VImage composited = tmpl.insert(
img_frame.extract_band(0, VImage::option()->set("n", 3)) img_frame.extract_band(0, VImage::option()->set("n", 3))
.bandjoin(255) .bandjoin(255)
.resize( .resize(
303.0 / (double)width, 303.0 / (double)width,
VImage::option()->set("vscale", 438.0 / (double)pageHeight)), VImage::option()->set("vscale", 438.0 / (double)pageHeight)),
310, 76); 310, 76);
img.push_back(composited); img.push_back(composited);
} }
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, 516); final.set(VIPS_META_PAGE_HEIGHT, 516);
void *buf; void *buf;
final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize);
return (char *)buf; ArgumentMap output;
output["buf"] = (char *)buf;
output["width"] = final.width();
output["height"] = vips_image_get_page_height(final.get_image());
return output;
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
using std::string; using std::string;
char* Zamn(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Zamn(string type, string* outType, char* BufferData, size_t BufferLength,
ArgumentMap Arguments, size_t* DataSize); ArgumentMap Arguments, size_t* DataSize);

View file

@ -50,7 +50,9 @@ export default function run(object) {
const result = img.image(object.cmd, objectWithFixedType); const result = img.image(object.cmd, objectWithFixedType);
const returnObject = { const returnObject = {
buffer: result.data, buffer: result.data,
fileExtension: result.type fileExtension: result.type,
width: result.width,
height: result.height
}; };
resolve(returnObject); resolve(returnObject);
} catch (e) { } catch (e) {

View file

@ -135,7 +135,9 @@ function waitForWorker(worker) {
worker.once("message", (data) => { worker.once("message", (data) => {
resolve({ resolve({
buffer: Buffer.from([...data.buffer]), buffer: Buffer.from([...data.buffer]),
type: data.fileExtension type: data.fileExtension,
width: data.width,
height: data.height
}); });
}); });
worker.once("error", reject); worker.once("error", reject);