Natives Rework (Thank you Essem)
Co-authored-by: Essem <TheEssem@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									b424b2f813
								
							
						
					
					
						commit
						ff7f0a3110
					
				
					 88 changed files with 3358 additions and 3104 deletions
				
			
		
							
								
								
									
										42
									
								
								app.js
									
										
									
									
									
								
							
							
						
						
									
										42
									
								
								app.js
									
										
									
									
									
								
							|  | @ -75,27 +75,27 @@ async function* getFiles(dir) { | |||
| 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); | ||||
|   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) { | ||||
|  |  | |||
|  | @ -79,14 +79,17 @@ class ImageCommand extends Command { | |||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       const { buffer, type } = await runImageJob(imageParams); | ||||
|       const { buffer, type, width, height } = await runImageJob(imageParams); | ||||
|       if (type === "nogif" && this.constructor.requiresGIF) { | ||||
|         return "That isn't a GIF!"; | ||||
|       } | ||||
|       this.success = true; | ||||
|       return { | ||||
|         contents: buffer, | ||||
|         name: `${this.constructor.command}.${type}` | ||||
|         name: `${this.constructor.command}.${type}`, | ||||
|         type, | ||||
|         width, | ||||
|         height | ||||
|       }; | ||||
|     } 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."; | ||||
|  |  | |||
|  | @ -1,6 +1,10 @@ | |||
| import ImageCommand from "../../classes/imageCommand.js"; | ||||
| 
 | ||||
| class ExplodeCommand extends ImageCommand { | ||||
|   params = { | ||||
|     implode: false | ||||
|   }; | ||||
| 
 | ||||
|   static category = "image-editing" | ||||
|   static description = "Explodes an image"; | ||||
|   static aliases = ["exp"]; | ||||
|  |  | |||
|  | @ -2,6 +2,9 @@ import ImageCommand from "../../classes/imageCommand.js"; | |||
| 
 | ||||
| class FlipCommand extends ImageCommand { | ||||
|   static category = "image-editing" | ||||
|   params = { | ||||
|     flop: false | ||||
|   }; | ||||
|   static description = "Flips an image"; | ||||
| 
 | ||||
|   static noImage = "You need to provide an image/GIF to flip!"; | ||||
|  |  | |||
|  | @ -99,11 +99,15 @@ export default async function (matrixClient, event, room, toStartOfTimeline) { | |||
|                 } else { | ||||
|                   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
 | ||||
|                   const imgsize = sizeOf(result.contents) | ||||
|                   let mime = imgsize.type; | ||||
|                   if (mime == "jpg") { | ||||
|                     mime = "jpeg"; | ||||
|                   // const imgsize = sizeOf(result.contents)
 | ||||
|                   const mime = result.type; | ||||
|                   const imgsize = { | ||||
|                     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) | ||||
|                 } | ||||
|               } else { | ||||
|  |  | |||
|  | @ -1,31 +1,36 @@ | |||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Blur(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool sharp = GetArgument<bool>(Arguments, "sharp"); | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   // TODO: find a better way to calculate the intensity for GIFs without
 | ||||
|   // splitting frames
 | ||||
|   VImage out = | ||||
|       sharp ? in.sharpen(VImage::option()->set("sigma", 3)) : in.gaussblur(15); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Blur(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool sharp = GetArgument<bool>(Arguments, "sharp"); | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   // TODO: find a better way to calculate the intensity for GIFs without
 | ||||
|   // splitting frames
 | ||||
|   VImage out = | ||||
|       sharp ? in.sharpen(VImage::option()->set("sigma", 3)) : in.gaussblur(15); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = out.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Blur(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Blur(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,51 +1,56 @@ | |||
| #include <math.h> | ||||
| 
 | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Bounce(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 15; | ||||
|   double mult = M_PI / nPages; | ||||
|   int halfHeight = pageHeight / 2; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     double height = halfHeight * ((abs(sin(i * mult)) * -1) + 1); | ||||
|     VImage embedded = | ||||
|         img_frame.embed(0, height, width, pageHeight + halfHeight); | ||||
|     img.push_back(embedded); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + halfHeight); | ||||
|   if (type != "gif") { | ||||
|     vector<int> delay(30, 50); | ||||
|     final.set("delay", delay); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <math.h> | ||||
| 
 | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Bounce(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 15; | ||||
|   double mult = M_PI / nPages; | ||||
|   int halfHeight = pageHeight / 2; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     double height = halfHeight * ((abs(sin(i * mult)) * -1) + 1); | ||||
|     VImage embedded = | ||||
|         img_frame.embed(0, height, width, pageHeight + halfHeight); | ||||
|     img.push_back(embedded); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + halfHeight); | ||||
|   if (type != "gif") { | ||||
|     vector<int> delay(30, 50); | ||||
|     final.set("delay", delay); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight + halfHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Bounce(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Bounce(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,77 +1,82 @@ | |||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Caption(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int size = width / 10; | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " + | ||||
|                        (font != "impact" ? "bold" : "normal") + " " + | ||||
|                        to_string(size); | ||||
| 
 | ||||
|   string captionText = "<span background=\"white\">" + caption + "</span>"; | ||||
| 
 | ||||
|   VImage text; | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     text = VImage::text( | ||||
|         ".", VImage::option()->set("fontfile", | ||||
|                                    (basePath + findResult->second).c_str())); | ||||
|   } | ||||
|   text = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", textWidth)); | ||||
|   VImage captionImage = | ||||
|       ((text == (vector<double>){0, 0, 0, 0}).bandand()) | ||||
|           .ifthenelse(255, text) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, text.height() + size, | ||||
|                    VImage::option()->set("extend", "white")); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage frame = captionImage.join( | ||||
|         img_frame, VIPS_DIRECTION_VERTICAL, | ||||
|         VImage::option()->set("background", 0xffffff)->set("expand", true)); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height()); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Caption(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int size = width / 10; | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " + | ||||
|                        (font != "impact" ? "bold" : "normal") + " " + | ||||
|                        to_string(size); | ||||
| 
 | ||||
|   string captionText = "<span background=\"white\">" + caption + "</span>"; | ||||
| 
 | ||||
|   VImage text; | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     text = VImage::text( | ||||
|         ".", VImage::option()->set("fontfile", | ||||
|                                    (basePath + findResult->second).c_str())); | ||||
|   } | ||||
|   text = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", textWidth)); | ||||
|   VImage captionImage = | ||||
|       ((text == (vector<double>){0, 0, 0, 0}).bandand()) | ||||
|           .ifthenelse(255, text) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, text.height() + size, | ||||
|                    VImage::option()->set("extend", "white")); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage frame = captionImage.join( | ||||
|         img_frame, VIPS_DIRECTION_VERTICAL, | ||||
|         VImage::option()->set("background", 0xffffff)->set("expand", true)); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height()); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Caption(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Caption(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|               ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,81 +1,86 @@ | |||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *CaptionTwo(string type, string *outType, char *BufferData, | ||||
|                  size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool top = GetArgument<bool>(Arguments, "top"); | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int size = width / 13; | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = (font == "roboto" ? "Roboto Condensed" : font) + | ||||
|                        ", Twemoji Color Emoji " + to_string(size); | ||||
| 
 | ||||
|   string captionText = "<span background=\"white\">" + caption + "</span>"; | ||||
| 
 | ||||
|   VImage text; | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     text = VImage::text( | ||||
|         ".", VImage::option()->set("fontfile", | ||||
|                                    (basePath + findResult->second).c_str())); | ||||
|   } | ||||
|   text = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("align", VIPS_ALIGN_LOW) | ||||
|           ->set("width", textWidth)); | ||||
|   VImage captionImage = | ||||
|       ((text == (vector<double>){0, 0, 0, 0}).bandand()) | ||||
|           .ifthenelse(255, text) | ||||
|           .embed(width / 25, width / 25, width, text.height() + size, | ||||
|                  VImage::option()->set("extend", "white")); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage frame = | ||||
|         (top ? captionImage : img_frame) | ||||
|             .join(top ? img_frame : captionImage, VIPS_DIRECTION_VERTICAL, | ||||
|                   VImage::option() | ||||
|                       ->set("background", 0xffffff) | ||||
|                       ->set("expand", true)); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height()); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap CaptionTwo(string type, string *outType, char *BufferData, | ||||
|                  size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool top = GetArgument<bool>(Arguments, "top"); | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int size = width / 13; | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = (font == "roboto" ? "Roboto Condensed" : font) + | ||||
|                        ", Twemoji Color Emoji " + to_string(size); | ||||
| 
 | ||||
|   string captionText = "<span background=\"white\">" + caption + "</span>"; | ||||
| 
 | ||||
|   VImage text; | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     text = VImage::text( | ||||
|         ".", VImage::option()->set("fontfile", | ||||
|                                    (basePath + findResult->second).c_str())); | ||||
|   } | ||||
|   text = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("align", VIPS_ALIGN_LOW) | ||||
|           ->set("width", textWidth)); | ||||
|   VImage captionImage = | ||||
|       ((text == (vector<double>){0, 0, 0, 0}).bandand()) | ||||
|           .ifthenelse(255, text) | ||||
|           .embed(width / 25, width / 25, width, text.height() + size, | ||||
|                  VImage::option()->set("extend", "white")); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage frame = | ||||
|         (top ? captionImage : img_frame) | ||||
|             .join(top ? img_frame : captionImage, VIPS_DIRECTION_VERTICAL, | ||||
|                   VImage::option() | ||||
|                       ->set("background", 0xffffff) | ||||
|                       ->set("expand", true)); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + captionImage.height()); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* CaptionTwo(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap CaptionTwo(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                  ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,54 +1,60 @@ | |||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| char *Circle(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> blurred; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     image.rotationalBlur(10); | ||||
|     image.magick(*outType); | ||||
|     blurred.push_back(image); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(blurred.begin(), blurred.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : blurred) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(blurred.begin(), blurred.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   // workaround because the data is tied to the blob
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
|   return data; | ||||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| ArgumentMap Circle(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> blurred; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     image.rotationalBlur(10); | ||||
|     image.magick(*outType); | ||||
|     blurred.push_back(image); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(blurred.begin(), blurred.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : blurred) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(blurred.begin(), blurred.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   // workaround because the data is tied to the blob
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
|    | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = data; | ||||
|   output["width"] = (int)blurred.front().columns(); | ||||
|   output["height"] = (int)blurred.front().rows(); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Circle(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Circle(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,26 +1,26 @@ | |||
| #include <cstring> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "../common.h" | ||||
| 
 | ||||
| void showUsage(char *path) { | ||||
| 	std::cout << "Usage: " << path << " operation [--arg=\"param\"] [...]" << std::endl; | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) { | ||||
|   if (argc < 1 || | ||||
|       (argc == 1 && !strcmp(argv[1], "-h"))) { | ||||
|     showUsage(argv[0]); | ||||
| #ifdef _WIN32 | ||||
|     system("PAUSE"); | ||||
| #endif | ||||
|     return 1; | ||||
|   } | ||||
| 
 | ||||
|   char *op = argv[1]; | ||||
| 
 | ||||
|   //handleArguments(argc, argv);
 | ||||
| 
 | ||||
|   std::cout << "This does nothing yet, but it might in the future!" << std::endl; | ||||
|   return 0; | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "../common.h" | ||||
| 
 | ||||
| void showUsage(char *path) { | ||||
| 	std::cout << "Usage: " << path << " operation [--arg=\"param\"] [...]" << std::endl; | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) { | ||||
|   if (argc < 1 || | ||||
|       (argc == 1 && !strcmp(argv[1], "-h"))) { | ||||
|     showUsage(argv[0]); | ||||
| #ifdef _WIN32 | ||||
|     system("PAUSE"); | ||||
| #endif | ||||
|     return 1; | ||||
|   } | ||||
| 
 | ||||
|   char *op = argv[1]; | ||||
| 
 | ||||
|   //handleArguments(argc, argv);
 | ||||
| 
 | ||||
|   std::cout << "This does nothing yet, but it might in the future!" << std::endl; | ||||
|   return 0; | ||||
| } | ||||
|  | @ -1,36 +1,41 @@ | |||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| char *Colors(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string color = GetArgument<string>(Arguments, "color"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   VImage out; | ||||
| 
 | ||||
|   if (color == "grayscale") { | ||||
|     out = in.colourspace(VIPS_INTERPRETATION_B_W); | ||||
|   } else if (color == "sepia") { | ||||
|     out = in.extract_band(0, VImage::option()->set("n", 3)).recomb(sepia); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| ArgumentMap Colors(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string color = GetArgument<string>(Arguments, "color"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   VImage out; | ||||
| 
 | ||||
|   if (color == "grayscale") { | ||||
|     out = in.colourspace(VIPS_INTERPRETATION_B_W); | ||||
|   } else if (color == "sepia") { | ||||
|     out = in.extract_band(0, VImage::option()->set("n", 3)).recomb(sepia); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = out.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
|    | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Colors(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Colors(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										258
									
								
								natives/common.h
									
										
									
									
									
								
							
							
						
						
									
										258
									
								
								natives/common.h
									
										
									
									
									
								
							|  | @ -1,130 +1,130 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <variant> | ||||
| 
 | ||||
| using std::map; | ||||
| using std::string; | ||||
| using std::variant; | ||||
| 
 | ||||
| typedef variant<string, float, bool, int> ArgumentVariant; | ||||
| typedef map<string, ArgumentVariant> ArgumentMap; | ||||
| 
 | ||||
| #include "blur.h" | ||||
| #include "bounce.h" | ||||
| #include "caption.h" | ||||
| #include "caption2.h" | ||||
| #include "circle.h" | ||||
| #include "colors.h" | ||||
| #include "crop.h" | ||||
| #include "deepfry.h" | ||||
| #include "explode.h" | ||||
| #include "flag.h" | ||||
| #include "flip.h" | ||||
| #include "freeze.h" | ||||
| #include "gamexplain.h" | ||||
| #include "globe.h" | ||||
| #include "homebrew.h" | ||||
| #include "invert.h" | ||||
| #include "jpeg.h" | ||||
| #include "magik.h" | ||||
| #include "meme.h" | ||||
| #include "mirror.h" | ||||
| #include "motivate.h" | ||||
| #include "reddit.h" | ||||
| #include "resize.h" | ||||
| #include "reverse.h" | ||||
| #include "scott.h" | ||||
| #include "snapchat.h" | ||||
| #include "sonic.h" | ||||
| #include "speed.h" | ||||
| #include "spin.h" | ||||
| #include "squish.h" | ||||
| #include "swirl.h" | ||||
| #include "tile.h" | ||||
| #include "togif.h" | ||||
| #include "uncanny.h" | ||||
| #include "uncaption.h" | ||||
| #include "wall.h" | ||||
| #include "watermark.h" | ||||
| #include "whisper.h" | ||||
| #include "zamn.h" | ||||
| 
 | ||||
| template <typename T> | ||||
| T GetArgument(ArgumentMap map, string key) { | ||||
|   try { | ||||
|     return std::get<T>(map.at(key)); | ||||
|   } catch (std::bad_variant_access&) { | ||||
|     throw "Invalid requested type from variant."; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| T GetArgumentWithFallback(ArgumentMap map, string key, T fallback) { | ||||
|   try { | ||||
|     return std::get<T>(map.at(key)); | ||||
|   } catch (...) {  // this is, not great...
 | ||||
|     return fallback; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| #define MAP_HAS(ARRAY, KEY) (ARRAY.count(KEY) > 0) | ||||
| #define MAP_GET(ARRAY, KEY, TYPE)                 \ | ||||
|   (MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) \ | ||||
|                        : NULL)  // C++ has forced my hand
 | ||||
| #define MAP_GET_FALLBACK(ARRAY, KEY, TYPE, FALLBACK) \ | ||||
|   (MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) : FALLBACK) | ||||
| 
 | ||||
| #define ARG_TYPES std::variant<string, bool, int, float> | ||||
| 
 | ||||
| const std::unordered_map<std::string, std::string> fontPaths{ | ||||
|     {"futura", "assets/fonts/caption.otf"}, | ||||
|     {"helvetica", "assets/fonts/caption2.ttf"}, | ||||
|     {"roboto", "assets/fonts/reddit.ttf"}}; | ||||
| 
 | ||||
| const std::map<std::string, | ||||
|                char* (*)(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                          ArgumentMap Arguments, size_t* DataSize)> | ||||
|     FunctionMap = {{"blur", &Blur}, | ||||
|                    {"bounce", &Bounce}, | ||||
|                    {"caption", &Caption}, | ||||
|                    {"captionTwo", &CaptionTwo}, | ||||
|                    {"circle", &Circle}, | ||||
|                    {"colors", &Colors}, | ||||
|                    {"crop", &Crop}, | ||||
|                    {"deepfry", &Deepfry}, | ||||
|                    {"explode", &Explode}, | ||||
|                    {"flag", &Flag}, | ||||
|                    {"flip", &Flip}, | ||||
|                    {"freeze", &Freeze}, | ||||
|                    {"gamexplain", Gamexplain}, | ||||
|                    {"globe", Globe}, | ||||
|                    {"invert", Invert}, | ||||
|                    {"jpeg", Jpeg}, | ||||
|                    {"magik", Magik}, | ||||
|                    {"meme", Meme}, | ||||
|                    {"mirror", Mirror}, | ||||
|                    {"motivate", Motivate}, | ||||
|                    {"reddit", Reddit}, | ||||
|                    {"resize", Resize}, | ||||
|                    {"reverse", Reverse}, | ||||
|                    {"scott", Scott}, | ||||
|                    {"snapchat", Snapchat}, | ||||
|                    {"speed", &Speed}, | ||||
|                    {"spin", Spin}, | ||||
|                    {"squish", Squish}, | ||||
|                    {"swirl", Swirl}, | ||||
|                    {"tile", Tile}, | ||||
|                    {"togif", ToGif}, | ||||
|                    {"uncanny", Uncanny}, | ||||
|                    {"uncaption", &Uncaption}, | ||||
|                    {"wall", Wall}, | ||||
|                    {"watermark", &Watermark}, | ||||
|                    {"whisper", Whisper}, | ||||
|                    {"zamn", Zamn}}; | ||||
| 
 | ||||
| const std::map<std::string, | ||||
|                char* (*)(string type, string* outType, ArgumentMap Arguments, size_t* DataSize)> | ||||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <variant> | ||||
| 
 | ||||
| using std::map; | ||||
| using std::string; | ||||
| using std::variant; | ||||
| 
 | ||||
| typedef variant<char*, string, float, bool, int> ArgumentVariant; | ||||
| typedef map<string, ArgumentVariant> ArgumentMap; | ||||
| 
 | ||||
| #include "blur.h" | ||||
| #include "bounce.h" | ||||
| #include "caption.h" | ||||
| #include "caption2.h" | ||||
| #include "circle.h" | ||||
| #include "colors.h" | ||||
| #include "crop.h" | ||||
| #include "deepfry.h" | ||||
| #include "explode.h" | ||||
| #include "flag.h" | ||||
| #include "flip.h" | ||||
| #include "freeze.h" | ||||
| #include "gamexplain.h" | ||||
| #include "globe.h" | ||||
| #include "homebrew.h" | ||||
| #include "invert.h" | ||||
| #include "jpeg.h" | ||||
| #include "magik.h" | ||||
| #include "meme.h" | ||||
| #include "mirror.h" | ||||
| #include "motivate.h" | ||||
| #include "reddit.h" | ||||
| #include "resize.h" | ||||
| #include "reverse.h" | ||||
| #include "scott.h" | ||||
| #include "snapchat.h" | ||||
| #include "sonic.h" | ||||
| #include "speed.h" | ||||
| #include "spin.h" | ||||
| #include "squish.h" | ||||
| #include "swirl.h" | ||||
| #include "tile.h" | ||||
| #include "togif.h" | ||||
| #include "uncanny.h" | ||||
| #include "uncaption.h" | ||||
| #include "wall.h" | ||||
| #include "watermark.h" | ||||
| #include "whisper.h" | ||||
| #include "zamn.h" | ||||
| 
 | ||||
| template <typename T> | ||||
| T GetArgument(ArgumentMap map, string key) { | ||||
|   try { | ||||
|     return std::get<T>(map.at(key)); | ||||
|   } catch (std::bad_variant_access&) { | ||||
|     throw "Invalid requested type from variant."; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| T GetArgumentWithFallback(ArgumentMap map, string key, T fallback) { | ||||
|   try { | ||||
|     return std::get<T>(map.at(key)); | ||||
|   } catch (...) {  // this is, not great...
 | ||||
|     return fallback; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| #define MAP_HAS(ARRAY, KEY) (ARRAY.count(KEY) > 0) | ||||
| #define MAP_GET(ARRAY, KEY, TYPE)                 \ | ||||
|   (MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) \ | ||||
|                        : NULL)  // C++ has forced my hand
 | ||||
| #define MAP_GET_FALLBACK(ARRAY, KEY, TYPE, FALLBACK) \ | ||||
|   (MAP_HAS(ARRAY, KEY) ? get<TYPE>(ARRAY.at(KEY)) : FALLBACK) | ||||
| 
 | ||||
| #define ARG_TYPES std::variant<string, bool, int, float> | ||||
| 
 | ||||
| const std::unordered_map<std::string, std::string> fontPaths{ | ||||
|     {"futura", "assets/fonts/caption.otf"}, | ||||
|     {"helvetica", "assets/fonts/caption2.ttf"}, | ||||
|     {"roboto", "assets/fonts/reddit.ttf"}}; | ||||
| 
 | ||||
| const std::map<std::string, | ||||
|                ArgumentMap (*)(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                          ArgumentMap Arguments, size_t* DataSize)> | ||||
|     FunctionMap = {{"blur", &Blur}, | ||||
|                    {"bounce", &Bounce}, | ||||
|                    {"caption", &Caption}, | ||||
|                    {"captionTwo", &CaptionTwo}, | ||||
|                    {"circle", &Circle}, | ||||
|                    {"colors", &Colors}, | ||||
|                    {"crop", &Crop}, | ||||
|                    {"deepfry", &Deepfry}, | ||||
|                    {"explode", &Explode}, | ||||
|                    {"flag", &Flag}, | ||||
|                    {"flip", &Flip}, | ||||
|                    {"freeze", &Freeze}, | ||||
|                    {"gamexplain", Gamexplain}, | ||||
|                    {"globe", Globe}, | ||||
|                    {"invert", Invert}, | ||||
|                    {"jpeg", Jpeg}, | ||||
|                    {"magik", Magik}, | ||||
|                    {"meme", Meme}, | ||||
|                    {"mirror", Mirror}, | ||||
|                    {"motivate", Motivate}, | ||||
|                    {"reddit", Reddit}, | ||||
|                    {"resize", Resize}, | ||||
|                    {"reverse", Reverse}, | ||||
|                    {"scott", Scott}, | ||||
|                    {"snapchat", Snapchat}, | ||||
|                    {"speed", &Speed}, | ||||
|                    {"spin", Spin}, | ||||
|                    {"squish", Squish}, | ||||
|                    {"swirl", Swirl}, | ||||
|                    {"tile", Tile}, | ||||
|                    {"togif", ToGif}, | ||||
|                    {"uncanny", Uncanny}, | ||||
|                    {"uncaption", &Uncaption}, | ||||
|                    {"wall", Wall}, | ||||
|                    {"watermark", &Watermark}, | ||||
|                    {"whisper", Whisper}, | ||||
|                    {"zamn", Zamn}}; | ||||
| 
 | ||||
| const std::map<std::string, | ||||
|                ArgumentMap (*)(string type, string* outType, ArgumentMap Arguments, size_t* DataSize)> | ||||
|     NoInputFunctionMap = {{"homebrew", Homebrew}, {"sonic", Sonic}}; | ||||
							
								
								
									
										105
									
								
								natives/crop.cc
									
										
									
									
									
								
							
							
						
						
									
										105
									
								
								natives/crop.cc
									
										
									
									
									
								
							|  | @ -1,51 +1,56 @@ | |||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Crop(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int finalHeight = 0; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     int frameWidth = img_frame.width(); | ||||
|     int frameHeight = img_frame.height(); | ||||
|     bool widthOrHeight = frameWidth / frameHeight >= 1; | ||||
|     int size = widthOrHeight ? frameHeight : frameWidth; | ||||
|     // img_frame.crop(frameWidth - size, frameHeight - size, size, size);
 | ||||
|     VImage result = img_frame.smartcrop( | ||||
|         size, size, | ||||
|         VImage::option()->set("interesting", VIPS_INTERESTING_CENTRE)); | ||||
|     finalHeight = size; | ||||
|     img.push_back(result); | ||||
|   } | ||||
| 
 | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, finalHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Crop(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int finalHeight = 0; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     int frameWidth = img_frame.width(); | ||||
|     int frameHeight = img_frame.height(); | ||||
|     bool widthOrHeight = frameWidth / frameHeight >= 1; | ||||
|     int size = widthOrHeight ? frameHeight : frameWidth; | ||||
|     // img_frame.crop(frameWidth - size, frameHeight - size, size, size);
 | ||||
|     VImage result = img_frame.smartcrop( | ||||
|         size, size, | ||||
|         VImage::option()->set("interesting", VIPS_INTERESTING_CENTRE)); | ||||
|     finalHeight = size; | ||||
|     img.push_back(result); | ||||
|   } | ||||
| 
 | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, finalHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = finalHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Crop(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Crop(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,61 +1,66 @@ | |||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Deepfry(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|               size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int totalHeight = in.height(); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   VImage fried = (in * 1.3 - (255.0 * 1.3 - 255.0)) * 1.5; | ||||
| 
 | ||||
|   VImage final; | ||||
|   if (totalHeight > 65500 && type == "gif") { | ||||
|     vector<VImage> img; | ||||
|     for (int i = 0; i < nPages; i++) { | ||||
|       VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|       void *jpgBuf; | ||||
|       size_t jpgLength; | ||||
|       img_frame.write_to_buffer( | ||||
|           ".jpg", &jpgBuf, &jpgLength, | ||||
|           VImage::option()->set("Q", 1)->set("strip", true)); | ||||
|       VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); | ||||
|       jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|       jpeged.set("delay", in.get_array_int("delay")); | ||||
|       img.push_back(jpeged); | ||||
|     } | ||||
|     final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|     final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   } else { | ||||
|     void *jpgBuf; | ||||
|     size_t jpgLength; | ||||
|     fried.write_to_buffer(".jpg", &jpgBuf, &jpgLength, | ||||
|                           VImage::option()->set("Q", 1)->set("strip", true)); | ||||
|     final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); | ||||
|     final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|     if (type == "gif") final.set("delay", fried.get_array_int("delay")); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("dither", 0) : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Deepfry(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|               size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int totalHeight = in.height(); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   VImage fried = (in * 1.3 - (255.0 * 1.3 - 255.0)) * 1.5; | ||||
| 
 | ||||
|   VImage final; | ||||
|   if (totalHeight > 65500 && type == "gif") { | ||||
|     vector<VImage> img; | ||||
|     for (int i = 0; i < nPages; i++) { | ||||
|       VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|       void *jpgBuf; | ||||
|       size_t jpgLength; | ||||
|       img_frame.write_to_buffer( | ||||
|           ".jpg", &jpgBuf, &jpgLength, | ||||
|           VImage::option()->set("Q", 1)->set("strip", true)); | ||||
|       VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); | ||||
|       jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|       jpeged.set("delay", in.get_array_int("delay")); | ||||
|       img.push_back(jpeged); | ||||
|     } | ||||
|     final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|     final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   } else { | ||||
|     void *jpgBuf; | ||||
|     size_t jpgLength; | ||||
|     fried.write_to_buffer(".jpg", &jpgBuf, &jpgLength, | ||||
|                           VImage::option()->set("Q", 1)->set("strip", true)); | ||||
|     final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); | ||||
|     final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|     if (type == "gif") final.set("delay", fried.get_array_int("delay")); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("dither", 0) : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Deepfry(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Deepfry(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|               ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,52 +1,57 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Explode(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool implode = GetArgumentWithFallback<bool>(Arguments, "implode", false); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string distortPath = basePath + "assets/images/" + | ||||
|                        (implode ? "linearimplode.png" : "linearexplode.png"); | ||||
|   VImage distort = | ||||
|       (VImage::new_from_file(distortPath.c_str()) | ||||
|            .resize(width / 500.0, VImage::option() | ||||
|                                       ->set("vscale", pageHeight / 500.0) | ||||
|                                       ->set("kernel", VIPS_KERNEL_CUBIC)) / | ||||
|        65535); | ||||
| 
 | ||||
|   VImage distortImage = (distort[0] * width).bandjoin(distort[1] * pageHeight); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage mapped = img_frame.mapim(distortImage); | ||||
|     img.push_back(mapped); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Explode(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool implode = GetArgumentWithFallback<bool>(Arguments, "implode", false); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string distortPath = basePath + "assets/images/" + | ||||
|                        (implode ? "linearimplode.png" : "linearexplode.png"); | ||||
|   VImage distort = | ||||
|       (VImage::new_from_file(distortPath.c_str()) | ||||
|            .resize(width / 500.0, VImage::option() | ||||
|                                       ->set("vscale", pageHeight / 500.0) | ||||
|                                       ->set("kernel", VIPS_KERNEL_CUBIC)) / | ||||
|        65535); | ||||
| 
 | ||||
|   VImage distortImage = (distort[0] * width).bandjoin(distort[1] * pageHeight); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage mapped = img_frame.mapim(distortImage); | ||||
|     img.push_back(mapped); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Explode(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Explode(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|               ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										101
									
								
								natives/flag.cc
									
										
									
									
									
								
							
							
						
						
									
										101
									
								
								natives/flag.cc
									
										
									
									
									
								
							|  | @ -1,49 +1,54 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Flag(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string overlay = GetArgument<string>(Arguments, "overlay"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string assetPath = basePath + overlay; | ||||
|   VImage overlayInput = VImage::new_from_file(assetPath.c_str()); | ||||
|   VImage overlayImage = overlayInput.resize( | ||||
|       (double)width / (double)overlayInput.width(), | ||||
|       VImage::option()->set( | ||||
|           "vscale", (double)pageHeight / (double)overlayInput.height())); | ||||
|   if (!overlayImage.has_alpha()) { | ||||
|     overlayImage = overlayImage.bandjoin(127); | ||||
|   } else { | ||||
|     // this is a pretty cool line, just saying
 | ||||
|     overlayImage = overlayImage * vector<double>{1, 1, 1, 0.5}; | ||||
|   } | ||||
|   VImage replicated = overlayImage.replicate(1, nPages); | ||||
|   VImage final = in.composite2(replicated, VIPS_BLEND_MODE_OVER); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Flag(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string overlay = GetArgument<string>(Arguments, "overlay"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string assetPath = basePath + overlay; | ||||
|   VImage overlayInput = VImage::new_from_file(assetPath.c_str()); | ||||
|   VImage overlayImage = overlayInput.resize( | ||||
|       (double)width / (double)overlayInput.width(), | ||||
|       VImage::option()->set( | ||||
|           "vscale", (double)pageHeight / (double)overlayInput.height())); | ||||
|   if (!overlayImage.has_alpha()) { | ||||
|     overlayImage = overlayImage.bandjoin(127); | ||||
|   } else { | ||||
|     // this is a pretty cool line, just saying
 | ||||
|     overlayImage = overlayImage * vector<double>{1, 1, 1, 0.5}; | ||||
|   } | ||||
|   VImage replicated = overlayImage.replicate(1, nPages); | ||||
|   VImage final = in.composite2(replicated, VIPS_BLEND_MODE_OVER); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Flag(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Flag(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,48 +1,53 @@ | |||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Flip(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool flop = GetArgument<bool>(Arguments, "flop"); | ||||
| 
 | ||||
|   VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                                       type == "gif" | ||||
|                                           ? VImage::option()->set("n", -1)->set( | ||||
|                                                 "access", "sequential") | ||||
|                                           : 0) | ||||
|                   .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   VImage out; | ||||
|   if (flop) { | ||||
|     out = in.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|   } else if (type == "gif") { | ||||
|     // libvips gif handling is both a blessing and a curse
 | ||||
|     vector<VImage> img; | ||||
|     int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|     int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|     for (int i = 0; i < nPages; i++) { | ||||
|       VImage img_frame = in.crop(0, i * pageHeight, in.width(), pageHeight); | ||||
|       VImage flipped = img_frame.flip(VIPS_DIRECTION_VERTICAL); | ||||
|       img.push_back(flipped); | ||||
|     } | ||||
|     out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|     out.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   } else { | ||||
|     out = in.flip(VIPS_DIRECTION_VERTICAL); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Flip(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool flop = GetArgumentWithFallback<bool>(Arguments, "flop", false); | ||||
| 
 | ||||
|   VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                                       type == "gif" | ||||
|                                           ? VImage::option()->set("n", -1)->set( | ||||
|                                                 "access", "sequential") | ||||
|                                           : 0) | ||||
|                   .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   VImage out; | ||||
|   if (flop) { | ||||
|     out = in.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|   } else if (type == "gif") { | ||||
|     // libvips gif handling is both a blessing and a curse
 | ||||
|     vector<VImage> img; | ||||
|     int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|     int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|     for (int i = 0; i < nPages; i++) { | ||||
|       VImage img_frame = in.crop(0, i * pageHeight, in.width(), pageHeight); | ||||
|       VImage flipped = img_frame.flip(VIPS_DIRECTION_VERTICAL); | ||||
|       img.push_back(flipped); | ||||
|     } | ||||
|     out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|     out.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   } else { | ||||
|     out = in.flip(VIPS_DIRECTION_VERTICAL); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = out.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Flip(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Flip(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,83 +1,107 @@ | |||
| #include <algorithm> | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Freeze(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool loop = GetArgumentWithFallback<bool>(Arguments, "loop", false); | ||||
|   int frame = GetArgumentWithFallback<int>(Arguments, "frame", -1); | ||||
| 
 | ||||
|   char *fileData = (char *)malloc(BufferLength); | ||||
|   memcpy(fileData, BufferData, BufferLength); | ||||
| 
 | ||||
|   char *match = (char *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01"; | ||||
|   char *descriptor = (char *)"\x2C\x00\x00\x00\x00"; | ||||
|   char *lastPos; | ||||
| 
 | ||||
|   bool none = true; | ||||
| 
 | ||||
|   if (loop) { | ||||
|     char *newData = (char *)malloc(BufferLength + 19); | ||||
|     memcpy(newData, fileData, BufferLength); | ||||
|     lastPos = (char *)memchr(newData, '\x2C', BufferLength); | ||||
|     while (lastPos != NULL) { | ||||
|       if (memcmp(lastPos, descriptor, 5) != 0) { | ||||
|         lastPos = (char *)memchr(lastPos + 1, '\x2C', | ||||
|                                  (BufferLength - (lastPos - newData)) - 1); | ||||
|         continue; | ||||
|       } | ||||
| 
 | ||||
|       memcpy(lastPos + 19, lastPos, (BufferLength - (lastPos - newData))); | ||||
|       memcpy(lastPos, match, 16); | ||||
|       memcpy(lastPos + 16, "\x00\x00\x00", 3); | ||||
| 
 | ||||
|       none = false; | ||||
|       *DataSize = BufferLength + 19; | ||||
|       break; | ||||
|     } | ||||
|     if (none) *DataSize = BufferLength; | ||||
| 
 | ||||
|     return newData; | ||||
|   } else if (frame >= 0 && !loop) { | ||||
|     VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|     VImage in = | ||||
|         VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                                 type == "gif" ? options->set("n", -1) : options) | ||||
|             .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|     if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|     int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|     int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|     int framePos = clamp(frame, 0, (int)nPages); | ||||
|     VImage out = in.crop(0, 0, in.width(), pageHeight * (framePos + 1)); | ||||
|     out.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|     out.set("loop", 1); | ||||
| 
 | ||||
|     void *buf; | ||||
|     out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|     return (char *)buf; | ||||
|   } 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; | ||||
| 
 | ||||
|     return fileData; | ||||
|   } | ||||
| } | ||||
| #include <algorithm> | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Freeze(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool loop = GetArgumentWithFallback<bool>(Arguments, "loop", false); | ||||
|   int frame = GetArgumentWithFallback<int>(Arguments, "frame", -1); | ||||
| 
 | ||||
|   char *fileData = (char *)malloc(BufferLength); | ||||
|   memcpy(fileData, BufferData, BufferLength); | ||||
| 
 | ||||
|   char *match = (char *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01"; | ||||
|   char *descriptor = (char *)"\x2C\x00\x00\x00\x00"; | ||||
|   char *lastPos; | ||||
| 
 | ||||
|   bool none = true; | ||||
| 
 | ||||
|   if (loop) { | ||||
|     char *newData = (char *)malloc(BufferLength + 19); | ||||
|     memcpy(newData, fileData, BufferLength); | ||||
|     lastPos = (char *)memchr(newData, '\x2C', BufferLength); | ||||
|     while (lastPos != NULL) { | ||||
|       if (memcmp(lastPos, descriptor, 5) != 0) { | ||||
|         lastPos = (char *)memchr(lastPos + 1, '\x2C', | ||||
|                                  (BufferLength - (lastPos - newData)) - 1); | ||||
|         continue; | ||||
|       } | ||||
| 
 | ||||
|       memcpy(lastPos + 19, lastPos, (BufferLength - (lastPos - newData))); | ||||
|       memcpy(lastPos, match, 16); | ||||
|       memcpy(lastPos + 16, "\x00\x00\x00", 3); | ||||
| 
 | ||||
|       none = false; | ||||
|       *DataSize = BufferLength + 19; | ||||
|       break; | ||||
|     } | ||||
|     if (none) *DataSize = BufferLength; | ||||
| 
 | ||||
|     VImage in = | ||||
|         VImage::new_from_buffer(newData, *DataSize, "", | ||||
|                                 VImage::option()->set("access", "sequential")); | ||||
| 
 | ||||
|     ArgumentMap output; | ||||
|     output["buf"] = newData; | ||||
|     output["width"] = in.width(); | ||||
|     output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|     return output; | ||||
|   } else if (frame >= 0 && !loop) { | ||||
|     VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|     VImage in = | ||||
|         VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                                 type == "gif" ? options->set("n", -1) : options) | ||||
|             .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|     if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|     int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|     int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|     int framePos = clamp(frame, 0, (int)nPages); | ||||
|     VImage out = in.crop(0, 0, in.width(), pageHeight * (framePos + 1)); | ||||
|     out.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|     out.set("loop", 1); | ||||
| 
 | ||||
|     void *buf; | ||||
|     out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|     ArgumentMap output; | ||||
|     output["buf"] = (char *)buf; | ||||
|     output["width"] = out.width(); | ||||
|     output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|     return output; | ||||
|      | ||||
|   } 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; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Freeze(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Freeze(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,50 +1,55 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Gamexplain(string type, string *outType, char *BufferData, | ||||
|                  size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/gamexplain.png"; | ||||
|   VImage tmpl = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = | ||||
|         img_frame | ||||
|             .resize(1181.0 / (double)width, | ||||
|                     VImage::option()->set("vscale", 571.0 / (double)pageHeight)) | ||||
|             .embed(10, 92, 1200, 675, VImage::option()->set("extend", "white")); | ||||
|     VImage composited = resized.composite2(tmpl, VIPS_BLEND_MODE_OVER); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 675); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Gamexplain(string type, string *outType, char *BufferData, | ||||
|                  size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/gamexplain.png"; | ||||
|   VImage tmpl = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = | ||||
|         img_frame | ||||
|             .resize(1181.0 / (double)width, | ||||
|                     VImage::option()->set("vscale", 571.0 / (double)pageHeight)) | ||||
|             .embed(10, 92, 1200, 675, VImage::option()->set("extend", "white")); | ||||
|     VImage composited = resized.composite2(tmpl, VIPS_BLEND_MODE_OVER); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 675); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Gamexplain(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Gamexplain(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                  ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										149
									
								
								natives/globe.cc
									
										
									
									
									
								
							
							
						
						
									
										149
									
								
								natives/globe.cc
									
										
									
									
									
								
							|  | @ -1,73 +1,78 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Globe(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                          : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; | ||||
| 
 | ||||
|   double size = min(width, pageHeight); | ||||
| 
 | ||||
|   string diffPath = basePath + "assets/images/globediffuse.png"; | ||||
|   VImage diffuse = VImage::new_from_file(diffPath.c_str()) | ||||
|                        .resize(size / 500.0, VImage::option()->set( | ||||
|                                                  "kernel", VIPS_KERNEL_CUBIC)) / | ||||
|                    255; | ||||
| 
 | ||||
|   string specPath = basePath + "assets/images/globespec.png"; | ||||
|   VImage specular = VImage::new_from_file(specPath.c_str()) | ||||
|                         .resize(size / 500.0, VImage::option()->set( | ||||
|                                                   "kernel", VIPS_KERNEL_CUBIC)); | ||||
| 
 | ||||
|   string distortPath = basePath + "assets/images/spheremap.png"; | ||||
|   VImage distort = | ||||
|       (VImage::new_from_file(distortPath.c_str()) | ||||
|            .resize(size / 500.0, | ||||
|                    VImage::option()->set("kernel", VIPS_KERNEL_CUBIC)) / | ||||
|        65535) * | ||||
|       size; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = img_frame.resize( | ||||
|         size / (double)width, | ||||
|         VImage::option()->set("vscale", size / (double)pageHeight)); | ||||
|     VImage rolled = img_frame.wrap( | ||||
|         VImage::option()->set("x", width * i / nPages)->set("y", 0)); | ||||
|     VImage extracted = rolled.extract_band(0, VImage::option()->set("n", 3)); | ||||
|     VImage mapped = extracted.mapim(distort); | ||||
|     VImage composited = mapped * diffuse + specular; | ||||
|     VImage frame = composited.bandjoin(diffuse > 0.0); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, size); | ||||
|   if (type != "gif") { | ||||
|     vector<int> delay(30, 50); | ||||
|     final.set("delay", delay); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Globe(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                          : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; | ||||
| 
 | ||||
|   double size = min(width, pageHeight); | ||||
| 
 | ||||
|   string diffPath = basePath + "assets/images/globediffuse.png"; | ||||
|   VImage diffuse = VImage::new_from_file(diffPath.c_str()) | ||||
|                        .resize(size / 500.0, VImage::option()->set( | ||||
|                                                  "kernel", VIPS_KERNEL_CUBIC)) / | ||||
|                    255; | ||||
| 
 | ||||
|   string specPath = basePath + "assets/images/globespec.png"; | ||||
|   VImage specular = VImage::new_from_file(specPath.c_str()) | ||||
|                         .resize(size / 500.0, VImage::option()->set( | ||||
|                                                   "kernel", VIPS_KERNEL_CUBIC)); | ||||
| 
 | ||||
|   string distortPath = basePath + "assets/images/spheremap.png"; | ||||
|   VImage distort = | ||||
|       (VImage::new_from_file(distortPath.c_str()) | ||||
|            .resize(size / 500.0, | ||||
|                    VImage::option()->set("kernel", VIPS_KERNEL_CUBIC)) / | ||||
|        65535) * | ||||
|       size; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = img_frame.resize( | ||||
|         size / (double)width, | ||||
|         VImage::option()->set("vscale", size / (double)pageHeight)); | ||||
|     VImage rolled = img_frame.wrap( | ||||
|         VImage::option()->set("x", width * i / nPages)->set("y", 0)); | ||||
|     VImage extracted = rolled.extract_band(0, VImage::option()->set("n", 3)); | ||||
|     VImage mapped = extracted.mapim(distort); | ||||
|     VImage composited = mapped * diffuse + specular; | ||||
|     VImage frame = composited.bandjoin(diffuse > 0.0); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, size); | ||||
|   if (type != "gif") { | ||||
|     vector<int> delay(30, 50); | ||||
|     final.set("delay", delay); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Globe(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Globe(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,37 +1,42 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Homebrew(string type, string *outType, ArgumentMap Arguments, | ||||
|                size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/hbc.png"; | ||||
|   VImage bg = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   VImage text = VImage::text( | ||||
|       ".", VImage::option()->set("fontfile", | ||||
|                                  (basePath + "assets/fonts/hbc.ttf").c_str())); | ||||
|   text = VImage::text( | ||||
|       ("<span letter_spacing=\"-5120\" color=\"white\">" + caption + "</span>") | ||||
|           .c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", "PF Square Sans Pro, Twemoji Color Font 96") | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())); | ||||
| 
 | ||||
|   VImage out = bg.composite2(text, VIPS_BLEND_MODE_OVER, | ||||
|                              VImage::option() | ||||
|                                  ->set("x", 400 - (text.width() / 2)) | ||||
|                                  ->set("y", 300 - (text.height() / 2) - 8)); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Homebrew(string type, string *outType, ArgumentMap Arguments, | ||||
|                size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/hbc.png"; | ||||
|   VImage bg = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   VImage text = VImage::text( | ||||
|       ".", VImage::option()->set("fontfile", | ||||
|                                  (basePath + "assets/fonts/hbc.ttf").c_str())); | ||||
|   text = VImage::text( | ||||
|       ("<span letter_spacing=\"-5120\" color=\"white\">" + caption + "</span>") | ||||
|           .c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", "PF Square Sans Pro, Twemoji Color Font 96") | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())); | ||||
| 
 | ||||
|   VImage out = bg.composite2(text, VIPS_BLEND_MODE_OVER, | ||||
|                              VImage::option() | ||||
|                                  ->set("x", 400 - (text.width() / 2)) | ||||
|                                  ->set("y", 300 - (text.height() / 2) - 8)); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = bg.width(); | ||||
|   output["height"] = vips_image_get_page_height(bg.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char *Homebrew(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Homebrew(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); | ||||
|  | @ -1,28 +1,33 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Invert(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   VImage noAlpha = | ||||
|       in.extract_band(0, VImage::option()->set("n", in.bands() - 1)); | ||||
|   VImage inverted = noAlpha.invert(); | ||||
|   VImage out = inverted.bandjoin(in.extract_band(3)); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Invert(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   VImage noAlpha = | ||||
|       in.extract_band(0, VImage::option()->set("n", in.bands() - 1)); | ||||
|   VImage inverted = noAlpha.invert(); | ||||
|   VImage out = inverted.bandjoin(in.extract_band(3)); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = out.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Invert(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Invert(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										158
									
								
								natives/jpeg.cc
									
										
									
									
									
								
							
							
						
						
									
										158
									
								
								natives/jpeg.cc
									
										
									
									
									
								
							|  | @ -1,75 +1,83 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Jpeg(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   int quality = GetArgumentWithFallback<int>(Arguments, "quality", 0); | ||||
| 
 | ||||
|   void *buf; | ||||
| 
 | ||||
|   if (type == "gif") { | ||||
|     VImage in = VImage::new_from_buffer( | ||||
|                     BufferData, BufferLength, "", | ||||
|                     VImage::option()->set("access", "sequential")->set("n", -1)) | ||||
|                     .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|     if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|     int width = in.width(); | ||||
|     int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|     int totalHeight = in.height(); | ||||
|     int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|     VImage final; | ||||
| 
 | ||||
|     if (totalHeight > 65500) { | ||||
|       vector<VImage> img; | ||||
|       for (int i = 0; i < nPages; i++) { | ||||
|         VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|         void *jpgBuf; | ||||
|         size_t jpgLength; | ||||
|         img_frame.write_to_buffer( | ||||
|             ".jpg", &jpgBuf, &jpgLength, | ||||
|             VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|         VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); | ||||
|         jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|         jpeged.set("delay", in.get_array_int("delay")); | ||||
|         img.push_back(jpeged); | ||||
|       } | ||||
|       final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|       final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|     } else { | ||||
|       void *jpgBuf; | ||||
|       size_t jpgLength; | ||||
|       in.write_to_buffer( | ||||
|           ".jpg", &jpgBuf, &jpgLength, | ||||
|           VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|       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); | ||||
|   } else { | ||||
|     VImage in = VImage::new_from_buffer(BufferData, BufferLength, ""); | ||||
|     void *jpgBuf; | ||||
|     in.write_to_buffer(".jpg", &jpgBuf, DataSize, | ||||
|                        VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|     if (*outType == "gif") { | ||||
|       VImage gifIn = VImage::new_from_buffer((char *)jpgBuf, *DataSize, ""); | ||||
|       gifIn.write_to_buffer( | ||||
|           ".gif", &buf, DataSize, | ||||
|           VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|     } else { | ||||
|       *outType = "jpg"; | ||||
|       buf = jpgBuf; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Jpeg(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   int quality = GetArgumentWithFallback<int>(Arguments, "quality", 0); | ||||
| 
 | ||||
|   void *buf; | ||||
|   ArgumentMap output; | ||||
| 
 | ||||
| 
 | ||||
|   if (type == "gif") { | ||||
|     VImage in = VImage::new_from_buffer( | ||||
|                     BufferData, BufferLength, "", | ||||
|                     VImage::option()->set("access", "sequential")->set("n", -1)) | ||||
|                     .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|     if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|     int width = in.width(); | ||||
|     int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|     int totalHeight = in.height(); | ||||
|     int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|     VImage final; | ||||
| 
 | ||||
|     if (totalHeight > 65500) { | ||||
|       vector<VImage> img; | ||||
|       for (int i = 0; i < nPages; i++) { | ||||
|         VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|         void *jpgBuf; | ||||
|         size_t jpgLength; | ||||
|         img_frame.write_to_buffer( | ||||
|             ".jpg", &jpgBuf, &jpgLength, | ||||
|             VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|         VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); | ||||
|         jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|         jpeged.set("delay", in.get_array_int("delay")); | ||||
|         img.push_back(jpeged); | ||||
|       } | ||||
|       final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|       final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|     } else { | ||||
|       void *jpgBuf; | ||||
|       size_t jpgLength; | ||||
|       in.write_to_buffer( | ||||
|           ".jpg", &jpgBuf, &jpgLength, | ||||
|           VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|       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); | ||||
|     output["buf"] = (char *)buf; | ||||
|     output["width"] = width; | ||||
|     output["height"] = pageHeight; | ||||
|   } else { | ||||
|     VImage in = VImage::new_from_buffer(BufferData, BufferLength, ""); | ||||
|     void *jpgBuf; | ||||
|     in.write_to_buffer(".jpg", &jpgBuf, DataSize, | ||||
|                        VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|     if (*outType == "gif") { | ||||
|       VImage gifIn = VImage::new_from_buffer((char *)jpgBuf, *DataSize, ""); | ||||
|       gifIn.write_to_buffer( | ||||
|           ".gif", &buf, DataSize, | ||||
|           VImage::option()->set("Q", quality)->set("strip", true)); | ||||
|     } else { | ||||
|       *outType = "jpg"; | ||||
|       buf = jpgBuf; | ||||
|     } | ||||
|     output["buf"] = (char *)buf; | ||||
|     output["width"] = in.width(); | ||||
|     output["height"] = vips_image_get_page_height(in.get_image()); | ||||
|   } | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Jpeg(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Jpeg(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										108
									
								
								natives/magik.cc
									
										
									
									
									
								
							
							
						
						
									
										108
									
								
								natives/magik.cc
									
										
									
									
									
								
							|  | @ -1,52 +1,58 @@ | |||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| char *Magik(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> blurred; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     image.scale(Geometry("350x350")); | ||||
|     image.liquidRescale(Geometry("175x175")); | ||||
|     image.liquidRescale(Geometry("350x350")); | ||||
|     image.magick(*outType); | ||||
|     blurred.push_back(image); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(blurred.begin(), blurred.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : blurred) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(blurred.begin(), blurred.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
|   return data; | ||||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| ArgumentMap Magik(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> blurred; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     image.scale(Geometry("350x350")); | ||||
|     image.liquidRescale(Geometry("175x175")); | ||||
|     image.liquidRescale(Geometry("350x350")); | ||||
|     image.magick(*outType); | ||||
|     blurred.push_back(image); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(blurred.begin(), blurred.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : blurred) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(blurred.begin(), blurred.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = data; | ||||
|   output["width"] = (int)blurred.front().columns(); | ||||
|   output["height"] = (int)blurred.front().rows(); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Magik(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Magik(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										281
									
								
								natives/meme.cc
									
										
									
									
									
								
							
							
						
						
									
										281
									
								
								natives/meme.cc
									
										
									
									
									
								
							|  | @ -1,139 +1,144 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Meme(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string top = GetArgument<string>(Arguments, "top"); | ||||
|   string bottom = GetArgument<string>(Arguments, "bottom"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int size = width / 9; | ||||
|   int dividedWidth = width / 1000; | ||||
|   int rad = 1; | ||||
|   vector<double> zeroVec = {0, 0, 0, 0}; | ||||
| 
 | ||||
|   string font_string = | ||||
|       (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font " + | ||||
|       (font != "impact" ? "bold" : "normal") + " " + to_string(size); | ||||
| 
 | ||||
|   VImage mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128; | ||||
|   mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true)); | ||||
| 
 | ||||
|   VImage altMask; | ||||
| 
 | ||||
|   if (dividedWidth >= 1) { | ||||
|     altMask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128; | ||||
|     altMask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth, | ||||
|                         VImage::option()->set("fill", true)); | ||||
|   } | ||||
| 
 | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     VImage::text(".", VImage::option()->set( | ||||
|                           "fontfile", (basePath + findResult->second).c_str())); | ||||
|   } | ||||
| 
 | ||||
|   VImage topText; | ||||
|   if (top != "") { | ||||
|     VImage topIn = VImage::text( | ||||
|         ("<span foreground=\"white\">" + top + "</span>").c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", font_string.c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", width)); | ||||
| 
 | ||||
|     topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20, | ||||
|                         (topIn.height() + 2 * rad) + 20); | ||||
| 
 | ||||
|     VImage topOutline = | ||||
|         topIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) | ||||
|             .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); | ||||
|     if (dividedWidth >= 1) { | ||||
|       topOutline = topOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE); | ||||
|     } | ||||
|     topOutline = (topOutline == zeroVec); | ||||
|     VImage topInvert = topOutline.extract_band(3).invert(); | ||||
|     topOutline = | ||||
|         topOutline | ||||
|             .extract_band(0, VImage::option()->set("n", topOutline.bands() - 1)) | ||||
|             .bandjoin(topInvert); | ||||
|     topText = topOutline.composite2(topIn, VIPS_BLEND_MODE_OVER); | ||||
|   } | ||||
| 
 | ||||
|   VImage bottomText; | ||||
|   if (bottom != "") { | ||||
|     VImage bottomIn = VImage::text( | ||||
|         ("<span foreground=\"white\">" + bottom + "</span>").c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", font_string.c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", width)); | ||||
|     bottomIn = | ||||
|         bottomIn.embed(rad + 10, rad + 10, (bottomIn.width() + 2 * rad) + 20, | ||||
|                        (bottomIn.height() + 2 * rad) + 20); | ||||
|     VImage bottomOutline = | ||||
|         bottomIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) | ||||
|             .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); | ||||
|     if (dividedWidth >= 1) { | ||||
|       bottomOutline = | ||||
|           bottomOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE); | ||||
|     } | ||||
|     bottomOutline = (bottomOutline == zeroVec); | ||||
|     VImage bottomInvert = bottomOutline.extract_band(3).invert(); | ||||
|     bottomOutline = bottomOutline | ||||
|                         .extract_band(0, VImage::option()->set( | ||||
|                                              "n", bottomOutline.bands() - 1)) | ||||
|                         .bandjoin(bottomInvert); | ||||
|     bottomText = bottomOutline.composite2(bottomIn, VIPS_BLEND_MODE_OVER); | ||||
|   } | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     if (top != "") { | ||||
|       img_frame = img_frame.composite2( | ||||
|           topText, VIPS_BLEND_MODE_OVER, | ||||
|           VImage::option()->set("x", (width / 2) - (topText.width() / 2))); | ||||
|     } | ||||
|     if (bottom != "") { | ||||
|       img_frame = img_frame.composite2( | ||||
|           bottomText, VIPS_BLEND_MODE_OVER, | ||||
|           VImage::option() | ||||
|               ->set("x", (width / 2) - (bottomText.width() / 2)) | ||||
|               ->set("y", pageHeight - bottomText.height())); | ||||
|     } | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Meme(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string top = GetArgument<string>(Arguments, "top"); | ||||
|   string bottom = GetArgument<string>(Arguments, "bottom"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int size = width / 9; | ||||
|   int dividedWidth = width / 1000; | ||||
|   int rad = 1; | ||||
|   vector<double> zeroVec = {0, 0, 0, 0}; | ||||
| 
 | ||||
|   string font_string = | ||||
|       (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font " + | ||||
|       (font != "impact" ? "bold" : "normal") + " " + to_string(size); | ||||
| 
 | ||||
|   VImage mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128; | ||||
|   mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true)); | ||||
| 
 | ||||
|   VImage altMask; | ||||
| 
 | ||||
|   if (dividedWidth >= 1) { | ||||
|     altMask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128; | ||||
|     altMask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth, | ||||
|                         VImage::option()->set("fill", true)); | ||||
|   } | ||||
| 
 | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     VImage::text(".", VImage::option()->set( | ||||
|                           "fontfile", (basePath + findResult->second).c_str())); | ||||
|   } | ||||
| 
 | ||||
|   VImage topText; | ||||
|   if (top != "") { | ||||
|     VImage topIn = VImage::text( | ||||
|         ("<span foreground=\"white\">" + top + "</span>").c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", font_string.c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", width)); | ||||
| 
 | ||||
|     topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20, | ||||
|                         (topIn.height() + 2 * rad) + 20); | ||||
| 
 | ||||
|     VImage topOutline = | ||||
|         topIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) | ||||
|             .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); | ||||
|     if (dividedWidth >= 1) { | ||||
|       topOutline = topOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE); | ||||
|     } | ||||
|     topOutline = (topOutline == zeroVec); | ||||
|     VImage topInvert = topOutline.extract_band(3).invert(); | ||||
|     topOutline = | ||||
|         topOutline | ||||
|             .extract_band(0, VImage::option()->set("n", topOutline.bands() - 1)) | ||||
|             .bandjoin(topInvert); | ||||
|     topText = topOutline.composite2(topIn, VIPS_BLEND_MODE_OVER); | ||||
|   } | ||||
| 
 | ||||
|   VImage bottomText; | ||||
|   if (bottom != "") { | ||||
|     VImage bottomIn = VImage::text( | ||||
|         ("<span foreground=\"white\">" + bottom + "</span>").c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", font_string.c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", width)); | ||||
|     bottomIn = | ||||
|         bottomIn.embed(rad + 10, rad + 10, (bottomIn.width() + 2 * rad) + 20, | ||||
|                        (bottomIn.height() + 2 * rad) + 20); | ||||
|     VImage bottomOutline = | ||||
|         bottomIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) | ||||
|             .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); | ||||
|     if (dividedWidth >= 1) { | ||||
|       bottomOutline = | ||||
|           bottomOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE); | ||||
|     } | ||||
|     bottomOutline = (bottomOutline == zeroVec); | ||||
|     VImage bottomInvert = bottomOutline.extract_band(3).invert(); | ||||
|     bottomOutline = bottomOutline | ||||
|                         .extract_band(0, VImage::option()->set( | ||||
|                                              "n", bottomOutline.bands() - 1)) | ||||
|                         .bandjoin(bottomInvert); | ||||
|     bottomText = bottomOutline.composite2(bottomIn, VIPS_BLEND_MODE_OVER); | ||||
|   } | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     if (top != "") { | ||||
|       img_frame = img_frame.composite2( | ||||
|           topText, VIPS_BLEND_MODE_OVER, | ||||
|           VImage::option()->set("x", (width / 2) - (topText.width() / 2))); | ||||
|     } | ||||
|     if (bottom != "") { | ||||
|       img_frame = img_frame.composite2( | ||||
|           bottomText, VIPS_BLEND_MODE_OVER, | ||||
|           VImage::option() | ||||
|               ->set("x", (width / 2) - (bottomText.width() / 2)) | ||||
|               ->set("y", pageHeight - bottomText.height())); | ||||
|     } | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Meme(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Meme(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,64 +1,69 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Mirror(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool vertical = GetArgumentWithFallback<bool>(Arguments, "vertical", false); | ||||
|   bool first = GetArgumentWithFallback<bool>(Arguments, "first", false); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   VImage out; | ||||
| 
 | ||||
|   if (vertical) { | ||||
|     if (type == "gif") { | ||||
|       // once again, libvips gif handling is both a blessing and a curse
 | ||||
|       vector<VImage> img; | ||||
|       int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|       int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|       bool isOdd = pageHeight % 2; | ||||
|       for (int i = 0; i < nPages; i++) { | ||||
|         int x = (i * pageHeight) + (first ? 0 : (pageHeight / 2)); | ||||
|         VImage cropped = in.crop(0, x, in.width(), pageHeight / 2); | ||||
|         VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); | ||||
|         VImage final = VImage::arrayjoin( | ||||
|             {first ? cropped : flipped, first ? flipped : cropped}, | ||||
|             VImage::option()->set("across", 1)); | ||||
|         img.push_back(final); | ||||
|       } | ||||
|       out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|       out.set(VIPS_META_PAGE_HEIGHT, pageHeight - (isOdd ? 1 : 0)); | ||||
|     } else { | ||||
|       VImage cropped = in.extract_area(0, 0, in.width(), in.height() / 2); | ||||
|       VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); | ||||
|       out = VImage::arrayjoin({cropped, flipped}, | ||||
|                               VImage::option()->set("across", 1)); | ||||
|     } | ||||
|   } else { | ||||
|     if (first) { | ||||
|       VImage cropped = in.extract_area(0, 0, in.width() / 2, in.height()); | ||||
|       VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|       out = VImage::arrayjoin({cropped, flipped}); | ||||
|     } else { | ||||
|       int size = in.width() / 2; | ||||
|       VImage cropped = in.extract_area(size, 0, size, in.height()); | ||||
|       VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|       out = VImage::arrayjoin({flipped, cropped}); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Mirror(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool vertical = GetArgumentWithFallback<bool>(Arguments, "vertical", false); | ||||
|   bool first = GetArgumentWithFallback<bool>(Arguments, "first", false); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   VImage out; | ||||
| 
 | ||||
|   if (vertical) { | ||||
|     if (type == "gif") { | ||||
|       // once again, libvips gif handling is both a blessing and a curse
 | ||||
|       vector<VImage> img; | ||||
|       int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|       int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|       bool isOdd = pageHeight % 2; | ||||
|       for (int i = 0; i < nPages; i++) { | ||||
|         int x = (i * pageHeight) + (first ? 0 : (pageHeight / 2)); | ||||
|         VImage cropped = in.crop(0, x, in.width(), pageHeight / 2); | ||||
|         VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); | ||||
|         VImage final = VImage::arrayjoin( | ||||
|             {first ? cropped : flipped, first ? flipped : cropped}, | ||||
|             VImage::option()->set("across", 1)); | ||||
|         img.push_back(final); | ||||
|       } | ||||
|       out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|       out.set(VIPS_META_PAGE_HEIGHT, pageHeight - (isOdd ? 1 : 0)); | ||||
|     } else { | ||||
|       VImage cropped = in.extract_area(0, 0, in.width(), in.height() / 2); | ||||
|       VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); | ||||
|       out = VImage::arrayjoin({cropped, flipped}, | ||||
|                               VImage::option()->set("across", 1)); | ||||
|     } | ||||
|   } else { | ||||
|     if (first) { | ||||
|       VImage cropped = in.extract_area(0, 0, in.width() / 2, in.height()); | ||||
|       VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|       out = VImage::arrayjoin({cropped, flipped}); | ||||
|     } else { | ||||
|       int size = in.width() / 2; | ||||
|       VImage cropped = in.extract_area(size, 0, size, in.height()); | ||||
|       VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|       out = VImage::arrayjoin({flipped, cropped}); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = in.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Mirror(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Mirror(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,123 +1,128 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Motivate(string type, string *outType, char *BufferData, | ||||
|                size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string top_text = GetArgument<string>(Arguments, "top"); | ||||
|   string bottom_text = GetArgument<string>(Arguments, "bottom"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int size = width / 5; | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = | ||||
|       (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font"; | ||||
| 
 | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     VImage::text(".", VImage::option()->set( | ||||
|                           "fontfile", (basePath + findResult->second).c_str())); | ||||
|   } | ||||
| 
 | ||||
|   VImage topImage; | ||||
|   if (top_text != "") { | ||||
|     string topText = "<span foreground=\"white\" background=\"black\">" + | ||||
|                      top_text + "</span>"; | ||||
| 
 | ||||
|     topImage = VImage::text( | ||||
|         topText.c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", (font_string + " " + to_string(size)).c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", textWidth)); | ||||
|   } | ||||
| 
 | ||||
|   VImage bottomImage; | ||||
|   if (bottom_text != "") { | ||||
|     string bottomText = "<span foreground=\"white\" background=\"black\">" + | ||||
|                         bottom_text + "</span>"; | ||||
| 
 | ||||
|     bottomImage = VImage::text( | ||||
|         bottomText.c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", (font_string + " " + to_string(size * 0.4)).c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", textWidth)); | ||||
|   } | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int height; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
| 
 | ||||
|     int borderSize = max(2, width / 66); | ||||
|     int borderSize2 = borderSize * 0.5; | ||||
|     VImage bordered = | ||||
|         img_frame.embed(borderSize, borderSize, width + (borderSize * 2), | ||||
|                         pageHeight + (borderSize * 2), | ||||
|                         VImage::option()->set("extend", "black")); | ||||
|     VImage bordered2 = bordered.embed(borderSize2, borderSize2, | ||||
|                                       bordered.width() + (borderSize2 * 2), | ||||
|                                       bordered.height() + (borderSize2 * 2), | ||||
|                                       VImage::option()->set("extend", "white")); | ||||
| 
 | ||||
|     int addition = width / 8; | ||||
|     int sideAddition = pageHeight * 0.4; | ||||
| 
 | ||||
|     VImage bordered3 = bordered2.embed( | ||||
|         sideAddition / 2, addition / 2, bordered2.width() + sideAddition, | ||||
|         bordered2.height() + addition, | ||||
|         VImage::option()->set("extend", "black")); | ||||
|     VImage frame; | ||||
|     if (top_text != "") { | ||||
|       frame = bordered3.join( | ||||
|           topImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(), | ||||
|                            topImage.height() + (size / 4), | ||||
|                            VImage::option()->set("extend", "black")), | ||||
|           VIPS_DIRECTION_VERTICAL, | ||||
|           VImage::option()->set("background", 0x000000)->set("expand", true)); | ||||
|     } | ||||
|     if (bottom_text != "") { | ||||
|       if (top_text == "") frame = bordered3; | ||||
|       frame = frame.join( | ||||
|           bottomImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(), | ||||
|                               bottomImage.height() + (size / 4), | ||||
|                               VImage::option()->set("extend", "black")), | ||||
|           VIPS_DIRECTION_VERTICAL, | ||||
|           VImage::option()->set("background", 0x000000)->set("expand", true)); | ||||
|     } | ||||
|     height = frame.height(); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)) | ||||
|                      .extract_band(0, VImage::option()->set("n", 3)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, height); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("dither", 1) : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Motivate(string type, string *outType, char *BufferData, | ||||
|                size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string top_text = GetArgument<string>(Arguments, "top"); | ||||
|   string bottom_text = GetArgument<string>(Arguments, "bottom"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int size = width / 5; | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = | ||||
|       (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font"; | ||||
| 
 | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     VImage::text(".", VImage::option()->set( | ||||
|                           "fontfile", (basePath + findResult->second).c_str())); | ||||
|   } | ||||
| 
 | ||||
|   VImage topImage; | ||||
|   if (top_text != "") { | ||||
|     string topText = "<span foreground=\"white\" background=\"black\">" + | ||||
|                      top_text + "</span>"; | ||||
| 
 | ||||
|     topImage = VImage::text( | ||||
|         topText.c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", (font_string + " " + to_string(size)).c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", textWidth)); | ||||
|   } | ||||
| 
 | ||||
|   VImage bottomImage; | ||||
|   if (bottom_text != "") { | ||||
|     string bottomText = "<span foreground=\"white\" background=\"black\">" + | ||||
|                         bottom_text + "</span>"; | ||||
| 
 | ||||
|     bottomImage = VImage::text( | ||||
|         bottomText.c_str(), | ||||
|         VImage::option() | ||||
|             ->set("rgba", true) | ||||
|             ->set("align", VIPS_ALIGN_CENTRE) | ||||
|             ->set("font", (font_string + " " + to_string(size * 0.4)).c_str()) | ||||
|             ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|             ->set("width", textWidth)); | ||||
|   } | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int height; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
| 
 | ||||
|     int borderSize = max(2, width / 66); | ||||
|     int borderSize2 = borderSize * 0.5; | ||||
|     VImage bordered = | ||||
|         img_frame.embed(borderSize, borderSize, width + (borderSize * 2), | ||||
|                         pageHeight + (borderSize * 2), | ||||
|                         VImage::option()->set("extend", "black")); | ||||
|     VImage bordered2 = bordered.embed(borderSize2, borderSize2, | ||||
|                                       bordered.width() + (borderSize2 * 2), | ||||
|                                       bordered.height() + (borderSize2 * 2), | ||||
|                                       VImage::option()->set("extend", "white")); | ||||
| 
 | ||||
|     int addition = width / 8; | ||||
|     int sideAddition = pageHeight * 0.4; | ||||
| 
 | ||||
|     VImage bordered3 = bordered2.embed( | ||||
|         sideAddition / 2, addition / 2, bordered2.width() + sideAddition, | ||||
|         bordered2.height() + addition, | ||||
|         VImage::option()->set("extend", "black")); | ||||
|     VImage frame; | ||||
|     if (top_text != "") { | ||||
|       frame = bordered3.join( | ||||
|           topImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(), | ||||
|                            topImage.height() + (size / 4), | ||||
|                            VImage::option()->set("extend", "black")), | ||||
|           VIPS_DIRECTION_VERTICAL, | ||||
|           VImage::option()->set("background", 0x000000)->set("expand", true)); | ||||
|     } | ||||
|     if (bottom_text != "") { | ||||
|       if (top_text == "") frame = bordered3; | ||||
|       frame = frame.join( | ||||
|           bottomImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(), | ||||
|                               bottomImage.height() + (size / 4), | ||||
|                               VImage::option()->set("extend", "black")), | ||||
|           VIPS_DIRECTION_VERTICAL, | ||||
|           VImage::option()->set("background", 0x000000)->set("expand", true)); | ||||
|     } | ||||
|     height = frame.height(); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)) | ||||
|                      .extract_band(0, VImage::option()->set("n", 3)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, height); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("dither", 1) : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = in.width(); | ||||
|   output["height"] = vips_image_get_page_height(in.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Motivate(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Motivate(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,124 +1,130 @@ | |||
| #include <napi.h> | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "../common.h" | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <Magick++.h> | ||||
| #endif | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| bool isNapiValueInt(Napi::Env& env, Napi::Value& num) { | ||||
|   return env.Global() | ||||
|       .Get("Number") | ||||
|       .ToObject() | ||||
|       .Get("isInteger") | ||||
|       .As<Napi::Function>() | ||||
|       .Call({num}) | ||||
|       .ToBoolean() | ||||
|       .Value(); | ||||
| } | ||||
| 
 | ||||
| Napi::Value ProcessImage(const Napi::CallbackInfo& info) { | ||||
|   Napi::Env env = info.Env(); | ||||
|   Napi::Object result = Napi::Object::New(env); | ||||
| 
 | ||||
|   try { | ||||
|     string command = info[0].As<Napi::String>().Utf8Value(); | ||||
|     Napi::Object obj = info[1].As<Napi::Object>(); | ||||
|     string type = | ||||
|         obj.Has("type") ? obj.Get("type").As<Napi::String>().Utf8Value() : "png"; | ||||
| 
 | ||||
|     Napi::Array properties = obj.GetPropertyNames(); | ||||
| 
 | ||||
|     ArgumentMap Arguments; | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < properties.Length(); i++) { | ||||
|       string property = | ||||
|           properties.Get(uint32_t(i)).As<Napi::String>().Utf8Value(); | ||||
| 
 | ||||
|       if (property == "data") { | ||||
|         continue; | ||||
|       } | ||||
| 
 | ||||
|       auto val = obj.Get(property); | ||||
|       if (val.IsBoolean()) { | ||||
|         Arguments[property] = val.ToBoolean().Value(); | ||||
|       } else if (val.IsString()) { | ||||
|         Arguments[property] = val.ToString().As<Napi::String>().Utf8Value(); | ||||
|       } else if (val.IsNumber()) { | ||||
|         auto num = val.ToNumber(); | ||||
|         if (isNapiValueInt(env, num)) { | ||||
|           Arguments[property] = num.Int32Value(); | ||||
|         } else { | ||||
|           Arguments[property] = num.FloatValue(); | ||||
|         } | ||||
|       } else { | ||||
|         throw "Unimplemented value type passed to image native."; | ||||
|         // Arguments[property] = val;
 | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     string outType = GetArgument<bool>(Arguments, "togif") ? "gif" : type; | ||||
| 
 | ||||
|     size_t length = 0; | ||||
|     char* buf; | ||||
|     if (obj.Has("data")) { | ||||
|       Napi::Buffer<char> data = obj.Has("data") | ||||
|                                     ? obj.Get("data").As<Napi::Buffer<char>>() | ||||
|                                     : Napi::Buffer<char>::New(env, 0); | ||||
|       buf = FunctionMap.at(command)(type, &outType, data.Data(), data.Length(), | ||||
|                                     Arguments, &length); | ||||
|     } else { | ||||
|       buf = NoInputFunctionMap.at(command)(type, &outType, Arguments, &length); | ||||
|     } | ||||
| 
 | ||||
|     vips_error_clear(); | ||||
|     vips_thread_shutdown(); | ||||
| 
 | ||||
|     result.Set("data", | ||||
|                Napi::Buffer<char>::New(env, buf, length, | ||||
|                                        []([[maybe_unused]] Napi::Env env, | ||||
|                                           void* data) { free(data); })); | ||||
|     result.Set("type", outType); | ||||
|   } catch (std::exception const& err) { | ||||
|     Napi::Error::New(env, err.what()).ThrowAsJavaScriptException(); | ||||
|   } catch (...) { | ||||
|     Napi::Error::New(env, "Unknown error").ThrowAsJavaScriptException(); | ||||
|   } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| Napi::Object Init(Napi::Env env, Napi::Object exports) { | ||||
| #ifdef _WIN32 | ||||
|   Magick::InitializeMagick(""); | ||||
| #endif | ||||
|   if (vips_init("")) vips_error_exit(NULL); | ||||
|   exports.Set(Napi::String::New(env, "image"), | ||||
|               Napi::Function::New(env, ProcessImage));  // new function handler
 | ||||
| 
 | ||||
|   Napi::Array arr = Napi::Array::New(env); | ||||
|   size_t i = 0; | ||||
|   for (auto const& imap : FunctionMap) { | ||||
|     Napi::HandleScope scope(env); | ||||
|     arr[i] = Napi::String::New(env, imap.first); | ||||
|     i++; | ||||
|   } | ||||
|   for (auto const& imap : NoInputFunctionMap) { | ||||
|     Napi::HandleScope scope(env); | ||||
|     arr[i] = Napi::String::New(env, imap.first); | ||||
|     i++; | ||||
|   } | ||||
| 
 | ||||
|   exports.Set(Napi::String::New(env, "funcs"), arr); | ||||
| 
 | ||||
|   return exports; | ||||
| } | ||||
| 
 | ||||
| NODE_API_MODULE(addon, Init) | ||||
| #include <napi.h> | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "../common.h" | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <Magick++.h> | ||||
| #endif | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| bool isNapiValueInt(Napi::Env& env, Napi::Value& num) { | ||||
|   return env.Global() | ||||
|       .Get("Number") | ||||
|       .ToObject() | ||||
|       .Get("isInteger") | ||||
|       .As<Napi::Function>() | ||||
|       .Call({num}) | ||||
|       .ToBoolean() | ||||
|       .Value(); | ||||
| } | ||||
| 
 | ||||
| Napi::Value ProcessImage(const Napi::CallbackInfo& info) { | ||||
|   Napi::Env env = info.Env(); | ||||
|   Napi::Object result = Napi::Object::New(env); | ||||
| 
 | ||||
|   try { | ||||
|     string command = info[0].As<Napi::String>().Utf8Value(); | ||||
|     Napi::Object obj = info[1].As<Napi::Object>(); | ||||
|     string type = | ||||
|         obj.Has("type") ? obj.Get("type").As<Napi::String>().Utf8Value() : "png"; | ||||
| 
 | ||||
|     Napi::Array properties = obj.GetPropertyNames(); | ||||
| 
 | ||||
|     ArgumentMap Arguments; | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < properties.Length(); i++) { | ||||
|       string property = | ||||
|           properties.Get(uint32_t(i)).As<Napi::String>().Utf8Value(); | ||||
| 
 | ||||
|       if (property == "data") { | ||||
|         continue; | ||||
|       } | ||||
| 
 | ||||
|       auto val = obj.Get(property); | ||||
|       if (val.IsBoolean()) { | ||||
|         Arguments[property] = val.ToBoolean().Value(); | ||||
|       } else if (val.IsString()) { | ||||
|         Arguments[property] = val.ToString().As<Napi::String>().Utf8Value(); | ||||
|       } else if (val.IsNumber()) { | ||||
|         auto num = val.ToNumber(); | ||||
|         if (isNapiValueInt(env, num)) { | ||||
|           Arguments[property] = num.Int32Value(); | ||||
|         } else { | ||||
|           Arguments[property] = num.FloatValue(); | ||||
|         } | ||||
|       } else { | ||||
|         throw "Unimplemented value type passed to image native."; | ||||
|         // Arguments[property] = val;
 | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     string outType = GetArgument<bool>(Arguments, "togif") ? "gif" : type; | ||||
| 
 | ||||
|     size_t length = 0; | ||||
|     ArgumentMap outMap; | ||||
|     if (obj.Has("data")) { | ||||
|       Napi::Buffer<char> data = obj.Has("data") | ||||
|                                     ? obj.Get("data").As<Napi::Buffer<char>>() | ||||
|                                     : Napi::Buffer<char>::New(env, 0); | ||||
|       outMap = FunctionMap.at(command)(type, &outType, data.Data(), data.Length(), | ||||
|                                     Arguments, &length); | ||||
|     } else { | ||||
|       outMap = NoInputFunctionMap.at(command)(type, &outType, Arguments, &length); | ||||
|     } | ||||
| 
 | ||||
|     vips_error_clear(); | ||||
|     vips_thread_shutdown(); | ||||
| 
 | ||||
|     char* buf = GetArgument<char*>(outMap, "buf"); | ||||
|     int width = GetArgument<int>(outMap, "width"); | ||||
|     int height = GetArgument<int>(outMap, "height"); | ||||
| 
 | ||||
|     result.Set("data", | ||||
|                Napi::Buffer<char>::New(env, buf, length, | ||||
|                                        []([[maybe_unused]] Napi::Env env, | ||||
|                                           void* data) { free(data); })); | ||||
|     result.Set("type", outType); | ||||
|     result.Set("width", width); | ||||
|     result.Set("height", height); | ||||
|   } catch (std::exception const& err) { | ||||
|     Napi::Error::New(env, err.what()).ThrowAsJavaScriptException(); | ||||
|   } catch (...) { | ||||
|     Napi::Error::New(env, "Unknown error").ThrowAsJavaScriptException(); | ||||
|   } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| Napi::Object Init(Napi::Env env, Napi::Object exports) { | ||||
| #ifdef _WIN32 | ||||
|   Magick::InitializeMagick(""); | ||||
| #endif | ||||
|   if (vips_init("")) vips_error_exit(NULL); | ||||
|   exports.Set(Napi::String::New(env, "image"), | ||||
|               Napi::Function::New(env, ProcessImage));  // new function handler
 | ||||
| 
 | ||||
|   Napi::Array arr = Napi::Array::New(env); | ||||
|   size_t i = 0; | ||||
|   for (auto const& imap : FunctionMap) { | ||||
|     Napi::HandleScope scope(env); | ||||
|     arr[i] = Napi::String::New(env, imap.first); | ||||
|     i++; | ||||
|   } | ||||
|   for (auto const& imap : NoInputFunctionMap) { | ||||
|     Napi::HandleScope scope(env); | ||||
|     arr[i] = Napi::String::New(env, imap.first); | ||||
|     i++; | ||||
|   } | ||||
| 
 | ||||
|   exports.Set(Napi::String::New(env, "funcs"), arr); | ||||
| 
 | ||||
|   return exports; | ||||
| } | ||||
| 
 | ||||
| NODE_API_MODULE(addon, Init) | ||||
|  |  | |||
|  | @ -1,67 +1,72 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Reddit(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string text = GetArgumentWithFallback<string>(Arguments, "text", ""); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/reddit.png"; | ||||
|   VImage tmpl = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string captionText = "<span foreground=\"white\">" + text + "</span>"; | ||||
| 
 | ||||
|   VImage textImage = VImage::text( | ||||
|       ".", VImage::option()->set( | ||||
|                "fontfile", (basePath + "assets/fonts/reddit.ttf").c_str())); | ||||
|   textImage = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("font", "Roboto, Twemoji Color Font 62") | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("align", VIPS_ALIGN_LOW)); | ||||
| 
 | ||||
|   VImage composited = | ||||
|       tmpl.composite2(textImage, VIPS_BLEND_MODE_OVER, | ||||
|                       VImage::option()->set("x", 375)->set( | ||||
|                           "y", (tmpl.height() - textImage.height()) - 64)); | ||||
|   VImage watermark = | ||||
|       composited.resize((double)width / (double)composited.width()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage frame = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, | ||||
|                                   VImage::option()->set("expand", true)); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + watermark.height()); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Reddit(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string text = GetArgument<string>(Arguments, "caption"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/reddit.png"; | ||||
|   VImage tmpl = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string captionText = "<span foreground=\"white\">" + text + "</span>"; | ||||
| 
 | ||||
|   VImage textImage = VImage::text( | ||||
|       ".", VImage::option()->set( | ||||
|                "fontfile", (basePath + "assets/fonts/reddit.ttf").c_str())); | ||||
|   textImage = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("font", "Roboto, Twemoji Color Font 62") | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("align", VIPS_ALIGN_LOW)); | ||||
| 
 | ||||
|   VImage composited = | ||||
|       tmpl.composite2(textImage, VIPS_BLEND_MODE_OVER, | ||||
|                       VImage::option()->set("x", 375)->set( | ||||
|                           "y", (tmpl.height() - textImage.height()) - 64)); | ||||
|   VImage watermark = | ||||
|       composited.resize((double)width / (double)composited.width()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage frame = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, | ||||
|                                   VImage::option()->set("expand", true)); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + watermark.height()); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight + watermark.height(); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Reddit(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Reddit(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,54 +1,59 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Resize(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool stretch = GetArgumentWithFallback<bool>(Arguments, "stretch", false); | ||||
|   bool wide = GetArgumentWithFallback<bool>(Arguments, "wide", false); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   VImage out; | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   int finalHeight; | ||||
|   if (stretch) { | ||||
|     out = | ||||
|         in.resize(512.0 / (double)width, | ||||
|                   VImage::option()->set("vscale", 512.0 / (double)pageHeight)); | ||||
|     finalHeight = 512; | ||||
|   } else if (wide) { | ||||
|     out = in.resize(9.5, VImage::option()->set("vscale", 0.5)); | ||||
|     finalHeight = pageHeight / 2; | ||||
|   } else { | ||||
|     // Pain. Pain. Pain. Pain. Pain.
 | ||||
|     vector<VImage> img; | ||||
|     for (int i = 0; i < nPages; i++) { | ||||
|       VImage img_frame = | ||||
|           type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|       VImage resized = img_frame.resize(0.1).resize( | ||||
|           10, VImage::option()->set("kernel", VIPS_KERNEL_NEAREST)); | ||||
|       img.push_back(resized); | ||||
|       finalHeight = resized.height(); | ||||
|     } | ||||
|     out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   } | ||||
|   out.set(VIPS_META_PAGE_HEIGHT, finalHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Resize(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool stretch = GetArgumentWithFallback<bool>(Arguments, "stretch", false); | ||||
|   bool wide = GetArgumentWithFallback<bool>(Arguments, "wide", false); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   VImage out; | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   int finalHeight; | ||||
|   if (stretch) { | ||||
|     out = | ||||
|         in.resize(512.0 / (double)width, | ||||
|                   VImage::option()->set("vscale", 512.0 / (double)pageHeight)); | ||||
|     finalHeight = 512; | ||||
|   } else if (wide) { | ||||
|     out = in.resize(9.5, VImage::option()->set("vscale", 0.5)); | ||||
|     finalHeight = pageHeight / 2; | ||||
|   } else { | ||||
|     // Pain. Pain. Pain. Pain. Pain.
 | ||||
|     vector<VImage> img; | ||||
|     for (int i = 0; i < nPages; i++) { | ||||
|       VImage img_frame = | ||||
|           type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|       VImage resized = img_frame.resize(0.1).resize( | ||||
|           10, VImage::option()->set("kernel", VIPS_KERNEL_NEAREST)); | ||||
|       img.push_back(resized); | ||||
|       finalHeight = resized.height(); | ||||
|     } | ||||
|     out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   } | ||||
|   out.set(VIPS_META_PAGE_HEIGHT, finalHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = finalHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Resize(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Resize(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,59 +1,64 @@ | |||
| #include <algorithm> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Reverse(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool soos = GetArgumentWithFallback<bool>(Arguments, "soos", false); | ||||
| 
 | ||||
|   VOption *options = | ||||
|       VImage::option()->set("access", "sequential")->set("n", -1); | ||||
| 
 | ||||
|   VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", options) | ||||
|                   .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   vector<VImage> split; | ||||
|   // todo: find a better way of getting individual frames (or at least getting
 | ||||
|   // the frames in reverse order)
 | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|     split.push_back(img_frame); | ||||
|   } | ||||
| 
 | ||||
|   vector<int> delays = in.get_array_int("delay"); | ||||
|   if (soos) { | ||||
|     vector<VImage> copy = split; | ||||
|     vector<int> copy2 = delays; | ||||
|     reverse(copy.begin(), copy.end()); | ||||
|     reverse(copy2.begin(), copy2.end()); | ||||
|     copy.pop_back(); | ||||
|     copy2.pop_back(); | ||||
|     copy.erase(copy.begin()); | ||||
|     copy2.erase(copy2.begin()); | ||||
|     split.insert(split.end(), copy.begin(), copy.end()); | ||||
|     delays.insert(delays.end(), copy2.begin(), copy2.end()); | ||||
|   } else { | ||||
|     reverse(split.begin(), split.end()); | ||||
|     reverse(delays.begin(), delays.end()); | ||||
|   } | ||||
| 
 | ||||
|   VImage final = VImage::arrayjoin(split, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   final.set("delay", delays); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize, | ||||
|                         VImage::option()->set("dither", 0)); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <algorithm> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Reverse(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool soos = GetArgumentWithFallback<bool>(Arguments, "soos", false); | ||||
| 
 | ||||
|   VOption *options = | ||||
|       VImage::option()->set("access", "sequential")->set("n", -1); | ||||
| 
 | ||||
|   VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", options) | ||||
|                   .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   vector<VImage> split; | ||||
|   // todo: find a better way of getting individual frames (or at least getting
 | ||||
|   // the frames in reverse order)
 | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|     split.push_back(img_frame); | ||||
|   } | ||||
| 
 | ||||
|   vector<int> delays = in.get_array_int("delay"); | ||||
|   if (soos) { | ||||
|     vector<VImage> copy = split; | ||||
|     vector<int> copy2 = delays; | ||||
|     reverse(copy.begin(), copy.end()); | ||||
|     reverse(copy2.begin(), copy2.end()); | ||||
|     copy.pop_back(); | ||||
|     copy2.pop_back(); | ||||
|     copy.erase(copy.begin()); | ||||
|     copy2.erase(copy2.begin()); | ||||
|     split.insert(split.end(), copy.begin(), copy.end()); | ||||
|     delays.insert(delays.end(), copy2.begin(), copy2.end()); | ||||
|   } else { | ||||
|     reverse(split.begin(), split.end()); | ||||
|     reverse(delays.begin(), delays.end()); | ||||
|   } | ||||
| 
 | ||||
|   VImage final = VImage::arrayjoin(split, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   final.set("delay", delays); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize, | ||||
|                         VImage::option()->set("dither", 0)); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Reverse(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Reverse(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|               ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										114
									
								
								natives/scott.cc
									
										
									
									
									
								
							
							
						
						
									
										114
									
								
								natives/scott.cc
									
										
									
									
									
								
							|  | @ -1,55 +1,61 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Scott(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/scott.png"; | ||||
|   VImage bg = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   string distortPath = basePath + "assets/images/scottmap.png"; | ||||
|   VImage distort = VImage::new_from_file(distortPath.c_str()); | ||||
| 
 | ||||
|   VImage distortImage = | ||||
|       ((distort[1] / 255) * 414).bandjoin((distort[0] / 255) * 233); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = img_frame.resize( | ||||
|         415 / (double)width, | ||||
|         VImage::option()->set("vscale", 234 / (double)pageHeight)); | ||||
|     VImage mapped = resized.mapim(distortImage) | ||||
|                         .extract_band(0, VImage::option()->set("n", 3)) | ||||
|                         .bandjoin(distort[2]); | ||||
|     VImage offset = mapped.embed(127, 181, 864, 481); | ||||
|     VImage composited = bg.composite2(offset, VIPS_BLEND_MODE_OVER); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 481); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("dither", 1) : 0); | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Scott(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/scott.png"; | ||||
|   VImage bg = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   string distortPath = basePath + "assets/images/scottmap.png"; | ||||
|   VImage distort = VImage::new_from_file(distortPath.c_str()); | ||||
| 
 | ||||
|   VImage distortImage = | ||||
|       ((distort[1] / 255) * 414).bandjoin((distort[0] / 255) * 233); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = img_frame.resize( | ||||
|         415 / (double)width, | ||||
|         VImage::option()->set("vscale", 234 / (double)pageHeight)); | ||||
|     VImage mapped = resized.mapim(distortImage) | ||||
|                         .extract_band(0, VImage::option()->set("n", 3)) | ||||
|                         .bandjoin(distort[2]); | ||||
|     VImage offset = mapped.embed(127, 181, 864, 481); | ||||
|     VImage composited = bg.composite2(offset, VIPS_BLEND_MODE_OVER); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 481); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("dither", 1) : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Scott(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Scott(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,72 +1,77 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Snapchat(string type, string *outType, char *BufferData, | ||||
|                size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   float pos = GetArgumentWithFallback<float>(Arguments, "pos", 0.5); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int size = width / 20; | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = "Helvetica Neue, Twemoji Color Font " + to_string(size); | ||||
| 
 | ||||
|   VImage textIn = VImage::text( | ||||
|       ".", VImage::option()->set( | ||||
|                "fontfile", (basePath + "assets/fonts/caption2.ttf").c_str())); | ||||
|   textIn = VImage::text( | ||||
|       ("<span foreground=\"white\" background=\"#000000B2\">" + caption + | ||||
|        "</span>") | ||||
|           .c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", textWidth)); | ||||
|   int bgHeight = textIn.height() + (width / 25); | ||||
|   textIn = ((textIn == (vector<double>){0, 0, 0, 0}).bandand()) | ||||
|                .ifthenelse({0, 0, 0, 178}, textIn) | ||||
|                .embed((width / 2) - (textIn.width() / 2), | ||||
|                       (bgHeight / 2) - (textIn.height() / 2), width, bgHeight, | ||||
|                       VImage::option() | ||||
|                           ->set("extend", "background") | ||||
|                           ->set("background", (vector<double>){0, 0, 0, 178})); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     img_frame = img_frame.composite2( | ||||
|         textIn, VIPS_BLEND_MODE_OVER, | ||||
|         VImage::option()->set("x", 0)->set("y", pageHeight * pos)); | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Snapchat(string type, string *outType, char *BufferData, | ||||
|                size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   float pos = GetArgumentWithFallback<float>(Arguments, "pos", 0.5); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int size = width / 20; | ||||
|   int textWidth = width - ((width / 25) * 2); | ||||
| 
 | ||||
|   string font_string = "Helvetica Neue, Twemoji Color Font " + to_string(size); | ||||
| 
 | ||||
|   VImage textIn = VImage::text( | ||||
|       ".", VImage::option()->set( | ||||
|                "fontfile", (basePath + "assets/fonts/caption2.ttf").c_str())); | ||||
|   textIn = VImage::text( | ||||
|       ("<span foreground=\"white\" background=\"#000000B2\">" + caption + | ||||
|        "</span>") | ||||
|           .c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", textWidth)); | ||||
|   int bgHeight = textIn.height() + (width / 25); | ||||
|   textIn = ((textIn == (vector<double>){0, 0, 0, 0}).bandand()) | ||||
|                .ifthenelse({0, 0, 0, 178}, textIn) | ||||
|                .embed((width / 2) - (textIn.width() / 2), | ||||
|                       (bgHeight / 2) - (textIn.height() / 2), width, bgHeight, | ||||
|                       VImage::option() | ||||
|                           ->set("extend", "background") | ||||
|                           ->set("background", (vector<double>){0, 0, 0, 178})); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     img_frame = img_frame.composite2( | ||||
|         textIn, VIPS_BLEND_MODE_OVER, | ||||
|         VImage::option()->set("x", 0)->set("y", pageHeight * pos)); | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Snapchat(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Snapchat(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,35 +1,40 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Sonic(string type, string *outType, ArgumentMap Arguments, | ||||
|             size_t *DataSize) { | ||||
|   string text = GetArgument<string>(Arguments, "text"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/sonic.jpg"; | ||||
|   VImage bg = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   VImage textImage = | ||||
|       VImage::text( | ||||
|           ("<span foreground=\"white\">" + text + "</span>").c_str(), | ||||
|           VImage::option() | ||||
|               ->set("rgba", true) | ||||
|               ->set("align", VIPS_ALIGN_CENTRE) | ||||
|               ->set("font", "Verdana, Twemoji Color Font") | ||||
|               ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|               ->set("width", 542) | ||||
|               ->set("height", 390)) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 542, 390); | ||||
| 
 | ||||
|   VImage out = bg.composite2(textImage, VIPS_BLEND_MODE_OVER, | ||||
|                              VImage::option()->set("x", 391)->set("y", 84)); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Sonic(string type, string *outType, ArgumentMap Arguments, | ||||
|             size_t *DataSize) { | ||||
|   string text = GetArgument<string>(Arguments, "text"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/sonic.jpg"; | ||||
|   VImage bg = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   VImage textImage = | ||||
|       VImage::text( | ||||
|           ("<span foreground=\"white\">" + text + "</span>").c_str(), | ||||
|           VImage::option() | ||||
|               ->set("rgba", true) | ||||
|               ->set("align", VIPS_ALIGN_CENTRE) | ||||
|               ->set("font", "Verdana, Twemoji Color Font") | ||||
|               ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|               ->set("width", 542) | ||||
|               ->set("height", 390)) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 542, 390); | ||||
| 
 | ||||
|   VImage out = bg.composite2(textImage, VIPS_BLEND_MODE_OVER, | ||||
|                              VImage::option()->set("x", 391)->set("y", 84)); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = out.width(); | ||||
|   output["height"] = vips_image_get_page_height(out.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char *Sonic(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Sonic(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); | ||||
							
								
								
									
										209
									
								
								natives/speed.cc
									
										
									
									
									
								
							
							
						
						
									
										209
									
								
								natives/speed.cc
									
										
									
									
									
								
							|  | @ -1,102 +1,109 @@ | |||
| #include <iostream> | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| void *memset16(void *m, uint16_t val, size_t count) { | ||||
|   uint16_t *buf = (uint16_t *)m; | ||||
| 
 | ||||
|   while (count--) *buf++ = val; | ||||
|   return m; | ||||
| } | ||||
| 
 | ||||
| char *vipsRemove(char *data, size_t length, size_t *DataSize, int speed) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = VImage::new_from_buffer(data, length, "", options->set("n", -1)) | ||||
|                   .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i += speed) { | ||||
|     VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   out.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| 
 | ||||
| char *Speed([[maybe_unused]] string type, string *outType, char *BufferData, | ||||
|             size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool slow = GetArgumentWithFallback<bool>(Arguments, "slow", false); | ||||
|   int speed = GetArgumentWithFallback<int>(Arguments, "speed", 2); | ||||
| 
 | ||||
|   char *fileData = (char *)malloc(BufferLength); | ||||
|   memcpy(fileData, BufferData, BufferLength); | ||||
| 
 | ||||
|   char *match = (char *)"\x00\x21\xF9\x04"; | ||||
| 
 | ||||
|   vector<uint16_t> old_delays; | ||||
|   bool removeFrames = false; | ||||
|   char *lastPos; | ||||
| 
 | ||||
|   // int amount = 0;
 | ||||
| 
 | ||||
|   lastPos = (char *)memchr(fileData, '\x00', BufferLength); | ||||
|   while (lastPos != NULL) { | ||||
|     if (memcmp(lastPos, match, 4) != 0) { | ||||
|       lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                                (BufferLength - (lastPos - fileData)) - 1); | ||||
|       continue; | ||||
|     } | ||||
|     //++amount;
 | ||||
|     uint16_t old_delay; | ||||
|     memcpy(&old_delay, lastPos + 5, 2); | ||||
|     old_delays.push_back(old_delay); | ||||
|     lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                              (BufferLength - (lastPos - fileData)) - 1); | ||||
|   } | ||||
| 
 | ||||
|   int currentFrame = 0; | ||||
|   lastPos = (char *)memchr(fileData, '\x00', BufferLength); | ||||
|   while (lastPos != NULL) { | ||||
|     if (memcmp(lastPos, match, 4) != 0) { | ||||
|       lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                                (BufferLength - (lastPos - fileData)) - 1); | ||||
|       continue; | ||||
|     } | ||||
|     uint16_t new_delay = slow ? old_delays[currentFrame] * speed | ||||
|                               : old_delays[currentFrame] / speed; | ||||
|     if (!slow && new_delay <= 1) { | ||||
|       removeFrames = true; | ||||
|       break; | ||||
|     } | ||||
| 
 | ||||
|     memset16(lastPos + 5, new_delay, 1); | ||||
| 
 | ||||
|     lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                              (BufferLength - (lastPos - fileData)) - 1); | ||||
|     ++currentFrame; | ||||
|   } | ||||
| 
 | ||||
|   if (removeFrames) { | ||||
|     fileData = vipsRemove(BufferData, BufferLength, DataSize, speed); | ||||
|   } else { | ||||
|     *DataSize = BufferLength; | ||||
|   } | ||||
| 
 | ||||
|   return fileData; | ||||
| #include <iostream> | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| void *memset16(void *m, uint16_t val, size_t count) { | ||||
|   uint16_t *buf = (uint16_t *)m; | ||||
| 
 | ||||
|   while (count--) *buf++ = val; | ||||
|   return m; | ||||
| } | ||||
| 
 | ||||
| ArgumentMap vipsHandle(char *data, size_t length, size_t *DataSize, int speed, bool removeFrames) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = VImage::new_from_buffer(data, length, "", options->set("n", -1)) | ||||
|                   .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   VImage out; | ||||
|   if (removeFrames) { | ||||
|     vector<VImage> img; | ||||
|     for (int i = 0; i < nPages; i += speed) { | ||||
|       VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); | ||||
|       img.push_back(img_frame); | ||||
|     } | ||||
|     out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|     out.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   } else { | ||||
|     out = in; | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   out.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
| 
 | ||||
| ArgumentMap Speed([[maybe_unused]] string type, string *outType, char *BufferData, | ||||
|             size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   bool slow = GetArgumentWithFallback<bool>(Arguments, "slow", false); | ||||
|   int speed = GetArgumentWithFallback<int>(Arguments, "speed", 2); | ||||
| 
 | ||||
|   char *fileData = (char *)malloc(BufferLength); | ||||
|   memcpy(fileData, BufferData, BufferLength); | ||||
| 
 | ||||
|   char *match = (char *)"\x00\x21\xF9\x04"; | ||||
| 
 | ||||
|   vector<uint16_t> old_delays; | ||||
|   bool removeFrames = false; | ||||
|   char *lastPos; | ||||
| 
 | ||||
|   // int amount = 0;
 | ||||
| 
 | ||||
|   lastPos = (char *)memchr(fileData, '\x00', BufferLength); | ||||
|   while (lastPos != NULL) { | ||||
|     if (memcmp(lastPos, match, 4) != 0) { | ||||
|       lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                                (BufferLength - (lastPos - fileData)) - 1); | ||||
|       continue; | ||||
|     } | ||||
|     //++amount;
 | ||||
|     uint16_t old_delay; | ||||
|     memcpy(&old_delay, lastPos + 5, 2); | ||||
|     old_delays.push_back(old_delay); | ||||
|     lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                              (BufferLength - (lastPos - fileData)) - 1); | ||||
|   } | ||||
| 
 | ||||
|   int currentFrame = 0; | ||||
|   lastPos = (char *)memchr(fileData, '\x00', BufferLength); | ||||
|   while (lastPos != NULL) { | ||||
|     if (memcmp(lastPos, match, 4) != 0) { | ||||
|       lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                                (BufferLength - (lastPos - fileData)) - 1); | ||||
|       continue; | ||||
|     } | ||||
|     uint16_t new_delay = slow ? old_delays[currentFrame] * speed | ||||
|                               : old_delays[currentFrame] / speed; | ||||
|     if (!slow && new_delay <= 1) { | ||||
|       removeFrames = true; | ||||
|       break; | ||||
|     } | ||||
| 
 | ||||
|     memset16(lastPos + 5, new_delay, 1); | ||||
| 
 | ||||
|     lastPos = (char *)memchr(lastPos + 1, '\x00', | ||||
|                              (BufferLength - (lastPos - fileData)) - 1); | ||||
|     ++currentFrame; | ||||
|   } | ||||
| 
 | ||||
|   // TODO: this is cursed, fix it later
 | ||||
|   ArgumentMap output = vipsHandle(BufferData, BufferLength, DataSize, speed, removeFrames); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Speed(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Speed(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										150
									
								
								natives/spin.cc
									
										
									
									
									
								
							
							
						
						
									
										150
									
								
								natives/spin.cc
									
										
									
									
									
								
							|  | @ -1,72 +1,78 @@ | |||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| char *Spin(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   int delay = GetArgumentWithFallback<int>(Arguments, "delay", 0); | ||||
| 
 | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> mid; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   if (type != "gif") { | ||||
|     list<Image>::iterator it = coalesced.begin(); | ||||
|     for (int i = 0; i < 29; ++i) { | ||||
|       coalesced.push_back(*it); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   int i = 0; | ||||
|   for (Image &image : coalesced) { | ||||
|     image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod); | ||||
|     image.scale(Geometry("256x256")); | ||||
|     image.alphaChannel(Magick::SetAlphaChannel); | ||||
|     double rotation[1] = {(double)360 * i / coalesced.size()}; | ||||
|     image.distort(Magick::ScaleRotateTranslateDistortion, 1, rotation); | ||||
|     image.magick("GIF"); | ||||
|     mid.push_back(image); | ||||
|     i++; | ||||
|   } | ||||
| 
 | ||||
|   for_each(mid.begin(), mid.end(), | ||||
|            gifDisposeMethodImage(Magick::BackgroundDispose)); | ||||
| 
 | ||||
|   optimizeTransparency(mid.begin(), mid.end()); | ||||
|   if (delay != 0) { | ||||
|     for_each(mid.begin(), mid.end(), animationDelayImage(delay)); | ||||
|   } else if (type != "gif") { | ||||
|     for_each(mid.begin(), mid.end(), animationDelayImage(5)); | ||||
|   } | ||||
| 
 | ||||
|   for (Image &image : mid) { | ||||
|     image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|     image.quantize(); | ||||
|   } | ||||
| 
 | ||||
|   writeImages(mid.begin(), mid.end(), &blob); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
|   return data; | ||||
| } | ||||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| ArgumentMap Spin(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   int delay = GetArgumentWithFallback<int>(Arguments, "delay", 0); | ||||
| 
 | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> mid; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   if (type != "gif") { | ||||
|     list<Image>::iterator it = coalesced.begin(); | ||||
|     for (int i = 0; i < 29; ++i) { | ||||
|       coalesced.push_back(*it); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   int i = 0; | ||||
|   for (Image &image : coalesced) { | ||||
|     image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod); | ||||
|     image.scale(Geometry("256x256")); | ||||
|     image.alphaChannel(Magick::SetAlphaChannel); | ||||
|     double rotation[1] = {(double)360 * i / coalesced.size()}; | ||||
|     image.distort(Magick::ScaleRotateTranslateDistortion, 1, rotation); | ||||
|     image.magick("GIF"); | ||||
|     mid.push_back(image); | ||||
|     i++; | ||||
|   } | ||||
| 
 | ||||
|   for_each(mid.begin(), mid.end(), | ||||
|            gifDisposeMethodImage(Magick::BackgroundDispose)); | ||||
| 
 | ||||
|   optimizeTransparency(mid.begin(), mid.end()); | ||||
|   if (delay != 0) { | ||||
|     for_each(mid.begin(), mid.end(), animationDelayImage(delay)); | ||||
|   } else if (type != "gif") { | ||||
|     for_each(mid.begin(), mid.end(), animationDelayImage(5)); | ||||
|   } | ||||
| 
 | ||||
|   for (Image &image : mid) { | ||||
|     image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|     image.quantize(); | ||||
|   } | ||||
| 
 | ||||
|   writeImages(mid.begin(), mid.end(), &blob); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
|    | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = data; | ||||
|   output["width"] = (int)mid.front().columns(); | ||||
|   output["height"] = (int)mid.front().rows(); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Spin(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Spin(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,52 +1,57 @@ | |||
| #include <math.h> | ||||
| 
 | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Squish(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; | ||||
|   double mult = (2 * M_PI) / nPages; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     double newWidth = (sin(i * mult) / 4) + 0.75; | ||||
|     double newHeight = (cos(i * mult) / 4) + 0.75; | ||||
|     VImage resized = | ||||
|         img_frame.resize(newWidth, VImage::option()->set("vscale", newHeight)) | ||||
|             .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, pageHeight); | ||||
|     img.push_back(resized); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   if (type != "gif") { | ||||
|     vector<int> delay(30, 50); | ||||
|     final.set("delay", delay); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <math.h> | ||||
| 
 | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Squish(string type, string *outType, char *BufferData, | ||||
|              size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, | ||||
|              size_t *DataSize) { | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; | ||||
|   double mult = (2 * M_PI) / nPages; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     double newWidth = (sin(i * mult) / 4) + 0.75; | ||||
|     double newHeight = (cos(i * mult) / 4) + 0.75; | ||||
|     VImage resized = | ||||
|         img_frame.resize(newWidth, VImage::option()->set("vscale", newHeight)) | ||||
|             .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, pageHeight); | ||||
|     img.push_back(resized); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
|   if (type != "gif") { | ||||
|     vector<int> delay(30, 50); | ||||
|     final.set("delay", delay); | ||||
|   } | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(".gif", &buf, DataSize); | ||||
| 
 | ||||
|   *outType = "gif"; | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(final.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Squish(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Squish(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|              ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										155
									
								
								natives/swirl.cc
									
										
									
									
									
								
							
							
						
						
									
										155
									
								
								natives/swirl.cc
									
										
									
									
									
								
							|  | @ -1,76 +1,81 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Swirl(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int width = in.width(); | ||||
|   double newWidth = width * 3; | ||||
|   double newHeight = pageHeight * 3; | ||||
|   vector<double> divSize = {newWidth / 2, newHeight / 2}; | ||||
| 
 | ||||
|   VImage index = VImage::xyz(newWidth, newHeight); | ||||
|   VImage center = index - divSize; | ||||
|   VImage polar = center | ||||
|                      .copy(VImage::option() | ||||
|                                ->set("format", VIPS_FORMAT_COMPLEX) | ||||
|                                ->set("bands", 1)) | ||||
|                      .polar() | ||||
|                      .copy(VImage::option() | ||||
|                                ->set("format", VIPS_FORMAT_FLOAT) | ||||
|                                ->set("bands", 2)); | ||||
| 
 | ||||
|   int size = min(width, pageHeight) / 2; | ||||
| 
 | ||||
|   VImage test = (1 - polar.extract_band(0) / size); | ||||
|   VImage degrees = test.cast(VIPS_FORMAT_FLOAT).pow(2); | ||||
| 
 | ||||
|   VImage angle = polar.extract_band(1) + degrees * 180; | ||||
| 
 | ||||
|   VImage distortion = polar.extract_band(0) | ||||
|                           .bandjoin(angle) | ||||
|                           .copy(VImage::option() | ||||
|                                     ->set("format", VIPS_FORMAT_COMPLEX) | ||||
|                                     ->set("bands", 1)) | ||||
|                           .rect() | ||||
|                           .copy(VImage::option() | ||||
|                                     ->set("format", VIPS_FORMAT_FLOAT) | ||||
|                                     ->set("bands", 2)) + | ||||
|                       divSize; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
| 
 | ||||
|     VImage distort = | ||||
|         img_frame | ||||
|             .gravity(VIPS_COMPASS_DIRECTION_CENTRE, newWidth, newHeight, | ||||
|                      VImage::option()->set("extend", VIPS_EXTEND_COPY)) | ||||
|             .mapim(distortion, | ||||
|                    VImage::option()->set( | ||||
|                        "interpolate", VInterpolate::new_from_name("bicubic"))); | ||||
|     VImage frame = distort.crop(width, pageHeight, width, pageHeight); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Swirl(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int width = in.width(); | ||||
|   double newWidth = width * 3; | ||||
|   double newHeight = pageHeight * 3; | ||||
|   vector<double> divSize = {newWidth / 2, newHeight / 2}; | ||||
| 
 | ||||
|   VImage index = VImage::xyz(newWidth, newHeight); | ||||
|   VImage center = index - divSize; | ||||
|   VImage polar = center | ||||
|                      .copy(VImage::option() | ||||
|                                ->set("format", VIPS_FORMAT_COMPLEX) | ||||
|                                ->set("bands", 1)) | ||||
|                      .polar() | ||||
|                      .copy(VImage::option() | ||||
|                                ->set("format", VIPS_FORMAT_FLOAT) | ||||
|                                ->set("bands", 2)); | ||||
| 
 | ||||
|   int size = min(width, pageHeight) / 2; | ||||
| 
 | ||||
|   VImage test = (1 - polar.extract_band(0) / size); | ||||
|   VImage degrees = test.cast(VIPS_FORMAT_FLOAT).pow(2); | ||||
| 
 | ||||
|   VImage angle = polar.extract_band(1) + degrees * 180; | ||||
| 
 | ||||
|   VImage distortion = polar.extract_band(0) | ||||
|                           .bandjoin(angle) | ||||
|                           .copy(VImage::option() | ||||
|                                     ->set("format", VIPS_FORMAT_COMPLEX) | ||||
|                                     ->set("bands", 1)) | ||||
|                           .rect() | ||||
|                           .copy(VImage::option() | ||||
|                                     ->set("format", VIPS_FORMAT_FLOAT) | ||||
|                                     ->set("bands", 2)) + | ||||
|                       divSize; | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
| 
 | ||||
|     VImage distort = | ||||
|         img_frame | ||||
|             .gravity(VIPS_COMPASS_DIRECTION_CENTRE, newWidth, newHeight, | ||||
|                      VImage::option()->set("extend", VIPS_EXTEND_COPY)) | ||||
|             .mapim(distortion, | ||||
|                    VImage::option()->set( | ||||
|                        "interpolate", VInterpolate::new_from_name("bicubic"))); | ||||
|     VImage frame = distort.crop(width, pageHeight, width, pageHeight); | ||||
|     img.push_back(frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(final.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Swirl(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Swirl(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										134
									
								
								natives/tile.cc
									
										
									
									
									
								
							
							
						
						
									
										134
									
								
								natives/tile.cc
									
										
									
									
									
								
							|  | @ -1,65 +1,71 @@ | |||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| char *Tile(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> mid; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     list<Image> duplicated; | ||||
|     Image appended; | ||||
|     list<Image> montage; | ||||
|     Image frame; | ||||
|     image.magick(*outType); | ||||
|     for (int i = 0; i < 5; ++i) { | ||||
|       duplicated.push_back(image); | ||||
|     } | ||||
|     appendImages(&appended, duplicated.begin(), duplicated.end()); | ||||
|     appended.repage(); | ||||
|     for (int i = 0; i < 5; ++i) { | ||||
|       montage.push_back(appended); | ||||
|     } | ||||
|     appendImages(&frame, montage.begin(), montage.end(), true); | ||||
|     frame.repage(); | ||||
|     frame.scale(Geometry("800x800>")); | ||||
|     frame.animationDelay(image.animationDelay()); | ||||
|     mid.push_back(frame); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(mid.begin(), mid.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : mid) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(mid.begin(), mid.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
|   return data; | ||||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| ArgumentMap Tile(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> mid; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     list<Image> duplicated; | ||||
|     Image appended; | ||||
|     list<Image> montage; | ||||
|     Image frame; | ||||
|     image.magick(*outType); | ||||
|     for (int i = 0; i < 5; ++i) { | ||||
|       duplicated.push_back(image); | ||||
|     } | ||||
|     appendImages(&appended, duplicated.begin(), duplicated.end()); | ||||
|     appended.repage(); | ||||
|     for (int i = 0; i < 5; ++i) { | ||||
|       montage.push_back(appended); | ||||
|     } | ||||
|     appendImages(&frame, montage.begin(), montage.end(), true); | ||||
|     frame.repage(); | ||||
|     frame.scale(Geometry("800x800>")); | ||||
|     frame.animationDelay(image.animationDelay()); | ||||
|     mid.push_back(frame); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(mid.begin(), mid.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : mid) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(mid.begin(), mid.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = data; | ||||
|   output["width"] = (int)mid.front().columns(); | ||||
|   output["height"] = (int)mid.front().rows(); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Tile(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Tile(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,28 +1,38 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *ToGif(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   if (type == "gif") { | ||||
|     *DataSize = BufferLength; | ||||
|     char *data = (char *)malloc(BufferLength); | ||||
|     memcpy(data, BufferData, BufferLength); | ||||
|     return data; | ||||
|   } else { | ||||
|     VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|     VImage in = VImage::new_from_buffer( | ||||
|         BufferData, BufferLength, "", | ||||
|         type == "webp" ? options->set("n", -1) : options); | ||||
| 
 | ||||
|     void *buf; | ||||
|     in.write_to_buffer(".gif", &buf, DataSize); | ||||
|     *outType = "gif"; | ||||
| 
 | ||||
|     return (char *)buf; | ||||
|   } | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap ToGif(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|             [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   if (type == "gif") { | ||||
|     *DataSize = BufferLength; | ||||
|     char *data = (char *)malloc(BufferLength); | ||||
|     memcpy(data, BufferData, BufferLength); | ||||
| 
 | ||||
|     ArgumentMap output; | ||||
|     output["buf"] = data; | ||||
| 
 | ||||
|     return output; | ||||
| 
 | ||||
|   } else { | ||||
|     VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|     VImage in = VImage::new_from_buffer( | ||||
|         BufferData, BufferLength, "", | ||||
|         type == "webp" ? options->set("n", -1) : options); | ||||
| 
 | ||||
|     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; | ||||
|   } | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* ToGif(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap ToGif(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|             ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,101 +1,106 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Uncanny(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string caption2 = GetArgument<string>(Arguments, "caption2"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string path = GetArgument<string>(Arguments, "path"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB) | ||||
|           .extract_band(0, VImage::option()->set("n", 3)); | ||||
| 
 | ||||
|   VImage base = VImage::black(1280, 720, VImage::option()->set("bands", 3)); | ||||
| 
 | ||||
|   string font_string = (font == "roboto" ? "Roboto Condensed" : font) + | ||||
|                        ", Twemoji Color Font " + | ||||
|                        (font != "impact" ? "bold" : "normal") + " 72"; | ||||
| 
 | ||||
|   string captionText = | ||||
|       "<span background=\"black\" foreground=\"white\">" + caption + "</span>"; | ||||
|   string caption2Text = | ||||
|       "<span background=\"black\" foreground=\"red\">" + caption2 + "</span>"; | ||||
| 
 | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     VImage::text(".", VImage::option()->set( | ||||
|                           "fontfile", (basePath + findResult->second).c_str())); | ||||
|   } | ||||
| 
 | ||||
|   VImage text = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", 588) | ||||
|           ->set("height", 90)); | ||||
|   VImage captionImage = | ||||
|       text.extract_band(0, VImage::option()->set("n", 3)) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, | ||||
|                    VImage::option()->set("extend", "black")); | ||||
| 
 | ||||
|   VImage text2 = VImage::text( | ||||
|       caption2Text.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", 588) | ||||
|           ->set("height", 90)); | ||||
|   VImage caption2Image = | ||||
|       text2.extract_band(0, VImage::option()->set("n", 3)) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, | ||||
|                    VImage::option()->set("extend", "black")); | ||||
| 
 | ||||
|   base = base.insert(captionImage, 0, 0).insert(caption2Image, 640, 0); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   VImage uncanny = VImage::new_from_file((basePath + path).c_str()); | ||||
| 
 | ||||
|   base = base.insert(uncanny, 0, 130); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = img_frame.resize(690.0 / (double)width); | ||||
|     if (resized.height() > 590) { | ||||
|       double vscale = 590.0 / (double)resized.height(); | ||||
|       resized = resized.resize(vscale, VImage::option()->set("vscale", vscale)); | ||||
|     } | ||||
|     VImage composited = base.insert(resized, 935 - (resized.width() / 2), | ||||
|                                     425 - (resized.height() / 2)); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 720); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("reoptimise", 1) : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Uncanny(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string caption2 = GetArgument<string>(Arguments, "caption2"); | ||||
|   string font = GetArgument<string>(Arguments, "font"); | ||||
|   string path = GetArgument<string>(Arguments, "path"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB) | ||||
|           .extract_band(0, VImage::option()->set("n", 3)); | ||||
| 
 | ||||
|   VImage base = VImage::black(1280, 720, VImage::option()->set("bands", 3)); | ||||
| 
 | ||||
|   string font_string = (font == "roboto" ? "Roboto Condensed" : font) + | ||||
|                        ", Twemoji Color Font " + | ||||
|                        (font != "impact" ? "bold" : "normal") + " 72"; | ||||
| 
 | ||||
|   string captionText = | ||||
|       "<span background=\"black\" foreground=\"white\">" + caption + "</span>"; | ||||
|   string caption2Text = | ||||
|       "<span background=\"black\" foreground=\"red\">" + caption2 + "</span>"; | ||||
| 
 | ||||
|   auto findResult = fontPaths.find(font); | ||||
|   if (findResult != fontPaths.end()) { | ||||
|     VImage::text(".", VImage::option()->set( | ||||
|                           "fontfile", (basePath + findResult->second).c_str())); | ||||
|   } | ||||
| 
 | ||||
|   VImage text = VImage::text( | ||||
|       captionText.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", 588) | ||||
|           ->set("height", 90)); | ||||
|   VImage captionImage = | ||||
|       text.extract_band(0, VImage::option()->set("n", 3)) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, | ||||
|                    VImage::option()->set("extend", "black")); | ||||
| 
 | ||||
|   VImage text2 = VImage::text( | ||||
|       caption2Text.c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", 588) | ||||
|           ->set("height", 90)); | ||||
|   VImage caption2Image = | ||||
|       text2.extract_band(0, VImage::option()->set("n", 3)) | ||||
|           .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, | ||||
|                    VImage::option()->set("extend", "black")); | ||||
| 
 | ||||
|   base = base.insert(captionImage, 0, 0).insert(caption2Image, 640, 0); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   VImage uncanny = VImage::new_from_file((basePath + path).c_str()); | ||||
| 
 | ||||
|   base = base.insert(uncanny, 0, 130); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage resized = img_frame.resize(690.0 / (double)width); | ||||
|     if (resized.height() > 590) { | ||||
|       double vscale = 590.0 / (double)resized.height(); | ||||
|       resized = resized.resize(vscale, VImage::option()->set("vscale", vscale)); | ||||
|     } | ||||
|     VImage composited = base.insert(resized, 935 - (resized.width() / 2), | ||||
|                                     425 - (resized.height() / 2)); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 720); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" ? VImage::option()->set("reoptimise", 1) : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(final.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Uncanny(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Uncanny(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|               ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,54 +1,59 @@ | |||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Uncaption(string type, string *outType, char *BufferData, | ||||
|                 size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   float tolerance = GetArgumentWithFallback<float>(Arguments, "tolerance", 0.5); | ||||
| 
 | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   VImage first = | ||||
|       in.crop(0, 0, 3, pageHeight).colourspace(VIPS_INTERPRETATION_B_W) > | ||||
|       (255 * tolerance); | ||||
|   int top, captionWidth, captionHeight; | ||||
|   first.find_trim(&top, &captionWidth, &captionHeight); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int newHeight = pageHeight - top; | ||||
|   if (top == pageHeight) { | ||||
|     newHeight = pageHeight; | ||||
|     top = 0; | ||||
|   } | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = in.crop(0, (i * pageHeight) + top, width, newHeight); | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, newHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Uncaption(string type, string *outType, char *BufferData, | ||||
|                 size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   float tolerance = GetArgumentWithFallback<float>(Arguments, "tolerance", 0.5); | ||||
| 
 | ||||
|   VOption *options = VImage::option(); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer( | ||||
|           BufferData, BufferLength, "", | ||||
|           type == "gif" ? options->set("n", -1)->set("access", "sequential") | ||||
|                         : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   VImage first = | ||||
|       in.crop(0, 0, 3, pageHeight).colourspace(VIPS_INTERPRETATION_B_W) > | ||||
|       (255 * tolerance); | ||||
|   int top, captionWidth, captionHeight; | ||||
|   first.find_trim(&top, &captionWidth, &captionHeight); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int newHeight = pageHeight - top; | ||||
|   if (top == pageHeight) { | ||||
|     newHeight = pageHeight; | ||||
|     top = 0; | ||||
|   } | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = in.crop(0, (i * pageHeight) + top, width, newHeight); | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, newHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(final.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Uncaption(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Uncaption(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                 ArgumentMap Arguments, size_t* DataSize); | ||||
							
								
								
									
										120
									
								
								natives/wall.cc
									
										
									
									
									
								
							
							
						
						
									
										120
									
								
								natives/wall.cc
									
										
									
									
									
								
							|  | @ -1,58 +1,64 @@ | |||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| char *Wall(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> mid; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     image.resize(Geometry("128x128")); | ||||
|     image.virtualPixelMethod(Magick::TileVirtualPixelMethod); | ||||
|     image.matteColor("none"); | ||||
|     image.backgroundColor("none"); | ||||
|     image.scale(Geometry("512x512")); | ||||
|     double arguments[16] = {0,   0, 57,  42, 0,   128, 63,  130, | ||||
|                             128, 0, 140, 60, 128, 128, 140, 140}; | ||||
|     image.distort(Magick::PerspectiveDistortion, 16, arguments); | ||||
|     image.scale(Geometry("800x800>")); | ||||
|     image.magick(*outType); | ||||
|     mid.push_back(image); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(mid.begin(), mid.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : mid) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(mid.begin(), mid.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
|   return data; | ||||
| #include <Magick++.h> | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace Magick; | ||||
| 
 | ||||
| ArgumentMap Wall(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { | ||||
|   Blob blob; | ||||
| 
 | ||||
|   list<Image> frames; | ||||
|   list<Image> coalesced; | ||||
|   list<Image> mid; | ||||
|   try { | ||||
|     readImages(&frames, Blob(BufferData, BufferLength)); | ||||
|   } catch (Magick::WarningCoder &warning) { | ||||
|     cerr << "Coder Warning: " << warning.what() << endl; | ||||
|   } catch (Magick::Warning &warning) { | ||||
|     cerr << "Warning: " << warning.what() << endl; | ||||
|   } | ||||
|   coalesceImages(&coalesced, frames.begin(), frames.end()); | ||||
| 
 | ||||
|   for (Image &image : coalesced) { | ||||
|     image.resize(Geometry("128x128")); | ||||
|     image.virtualPixelMethod(Magick::TileVirtualPixelMethod); | ||||
|     image.matteColor("none"); | ||||
|     image.backgroundColor("none"); | ||||
|     image.scale(Geometry("512x512")); | ||||
|     double arguments[16] = {0,   0, 57,  42, 0,   128, 63,  130, | ||||
|                             128, 0, 140, 60, 128, 128, 140, 140}; | ||||
|     image.distort(Magick::PerspectiveDistortion, 16, arguments); | ||||
|     image.scale(Geometry("800x800>")); | ||||
|     image.magick(*outType); | ||||
|     mid.push_back(image); | ||||
|   } | ||||
| 
 | ||||
|   optimizeTransparency(mid.begin(), mid.end()); | ||||
| 
 | ||||
|   if (*outType == "gif") { | ||||
|     for (Image &image : mid) { | ||||
|       image.quantizeDitherMethod(FloydSteinbergDitherMethod); | ||||
|       image.quantize(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   writeImages(mid.begin(), mid.end(), &blob); | ||||
| 
 | ||||
|   *DataSize = blob.length(); | ||||
| 
 | ||||
|   char *data = (char *)malloc(*DataSize); | ||||
|   memcpy(data, blob.data(), *DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = data; | ||||
|   output["width"] = (int)mid.front().columns(); | ||||
|   output["height"] = (int)mid.front().rows(); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Wall(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Wall(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,154 +1,159 @@ | |||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Watermark(string type, string *outType, char *BufferData, | ||||
|                 size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string water = GetArgument<string>(Arguments, "water"); | ||||
|   int gravity = GetArgument<int>(Arguments, "gravity"); | ||||
| 
 | ||||
|   bool resize = GetArgumentWithFallback<bool>(Arguments, "resize", false); | ||||
| 
 | ||||
|   float yscale = GetArgumentWithFallback<float>(Arguments, "yscale", false); | ||||
| 
 | ||||
|   bool append = GetArgumentWithFallback<bool>(Arguments, "append", false); | ||||
| 
 | ||||
|   bool alpha = GetArgumentWithFallback<bool>(Arguments, "alpha", false); | ||||
|   bool flip = GetArgumentWithFallback<bool>(Arguments, "flip", false); | ||||
| 
 | ||||
|   bool mc = MAP_HAS(Arguments, "mc"); | ||||
| 
 | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   string merged = basePath + water; | ||||
|   VImage watermark = VImage::new_from_file(merged.c_str()); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   if (flip) { | ||||
|     watermark = watermark.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|   } | ||||
| 
 | ||||
|   if (resize && append) { | ||||
|     watermark = watermark.resize((double)width / (double)watermark.width()); | ||||
|   } else if (resize && yscale) { | ||||
|     watermark = watermark.resize( | ||||
|         (double)width / (double)watermark.width(), | ||||
|         VImage::option()->set("vscale", (double)(pageHeight * yscale) / | ||||
|                                             (double)watermark.height())); | ||||
|   } else if (resize) { | ||||
|     watermark = | ||||
|         watermark.resize((double)pageHeight / (double)watermark.height()); | ||||
|   } | ||||
| 
 | ||||
|   int x = 0, y = 0; | ||||
|   switch (gravity) { | ||||
|     case 1: | ||||
|       break; | ||||
|     case 2: | ||||
|       x = (width / 2) - (watermark.width() / 2); | ||||
|       break; | ||||
|     case 3: | ||||
|       x = width - watermark.width(); | ||||
|       break; | ||||
|     case 5: | ||||
|       x = (width / 2) - (watermark.width() / 2); | ||||
|       y = (pageHeight / 2) - (watermark.height() / 2); | ||||
|       break; | ||||
|     case 6: | ||||
|       x = width - watermark.width(); | ||||
|       y = (pageHeight / 2) - (watermark.height() / 2); | ||||
|       break; | ||||
|     case 8: | ||||
|       x = (width / 2) - (watermark.width() / 2); | ||||
|       y = pageHeight - watermark.height(); | ||||
|       break; | ||||
|     case 9: | ||||
|       x = width - watermark.width(); | ||||
|       y = pageHeight - watermark.height(); | ||||
|       break; | ||||
|   } | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int addedHeight = 0; | ||||
|   VImage contentAlpha; | ||||
|   VImage frameAlpha; | ||||
|   VImage bg; | ||||
|   VImage frame; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     if (append) { | ||||
|       VImage appended = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, | ||||
|                                        VImage::option()->set("expand", true)); | ||||
|       addedHeight = watermark.height(); | ||||
|       img.push_back(appended); | ||||
|     } else if (mc) { | ||||
|       VImage padded = | ||||
|           img_frame.embed(0, 0, width, pageHeight + 15, | ||||
|                           VImage::option()->set("background", 0xffffff)); | ||||
|       VImage composited = | ||||
|           padded.composite2(watermark, VIPS_BLEND_MODE_OVER, | ||||
|                             VImage::option() | ||||
|                                 ->set("x", width - 190) | ||||
|                                 ->set("y", padded.height() - 22)); | ||||
|       addedHeight = 15; | ||||
|       img.push_back(composited); | ||||
|     } else { | ||||
|       VImage composited; | ||||
|       if (alpha) { | ||||
|         if (i == 0) { | ||||
|           contentAlpha = watermark.extract_band(0).embed( | ||||
|               x, y, width, pageHeight, | ||||
|               VImage::option()->set("extend", "white")); | ||||
|           frameAlpha = watermark.extract_band(1).embed( | ||||
|               x, y, width, pageHeight, | ||||
|               VImage::option()->set("extend", "black")); | ||||
|           bg = frameAlpha.new_from_image({0, 0, 0}).copy(VImage::option()->set( | ||||
|               "interpretation", VIPS_INTERPRETATION_sRGB)); | ||||
|           frame = bg.bandjoin(frameAlpha); | ||||
|           if (*outType == "jpg" || *outType == "jpeg") { | ||||
|             *outType = "png"; | ||||
|           } | ||||
|         } | ||||
|         VImage content = | ||||
|             img_frame.extract_band(0, VImage::option()->set("n", 3)) | ||||
|                 .bandjoin(contentAlpha & img_frame.extract_band(3)); | ||||
| 
 | ||||
|         composited = | ||||
|             content.composite2(frame, VIPS_BLEND_MODE_OVER, | ||||
|                                VImage::option()->set("x", x)->set("y", y)); | ||||
|       } else { | ||||
|         composited = | ||||
|             img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER, | ||||
|                                  VImage::option()->set("x", x)->set("y", y)); | ||||
|       } | ||||
|       img.push_back(composited); | ||||
|     } | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + addedHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| } | ||||
| #include <map> | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Watermark(string type, string *outType, char *BufferData, | ||||
|                 size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string water = GetArgument<string>(Arguments, "water"); | ||||
|   int gravity = GetArgument<int>(Arguments, "gravity"); | ||||
| 
 | ||||
|   bool resize = GetArgumentWithFallback<bool>(Arguments, "resize", false); | ||||
| 
 | ||||
|   float yscale = GetArgumentWithFallback<float>(Arguments, "yscale", false); | ||||
| 
 | ||||
|   bool append = GetArgumentWithFallback<bool>(Arguments, "append", false); | ||||
| 
 | ||||
|   bool alpha = GetArgumentWithFallback<bool>(Arguments, "alpha", false); | ||||
|   bool flip = GetArgumentWithFallback<bool>(Arguments, "flip", false); | ||||
| 
 | ||||
|   bool mc = MAP_HAS(Arguments, "mc"); | ||||
| 
 | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   string merged = basePath + water; | ||||
|   VImage watermark = VImage::new_from_file(merged.c_str()); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   if (flip) { | ||||
|     watermark = watermark.flip(VIPS_DIRECTION_HORIZONTAL); | ||||
|   } | ||||
| 
 | ||||
|   if (resize && append) { | ||||
|     watermark = watermark.resize((double)width / (double)watermark.width()); | ||||
|   } else if (resize && yscale) { | ||||
|     watermark = watermark.resize( | ||||
|         (double)width / (double)watermark.width(), | ||||
|         VImage::option()->set("vscale", (double)(pageHeight * yscale) / | ||||
|                                             (double)watermark.height())); | ||||
|   } else if (resize) { | ||||
|     watermark = | ||||
|         watermark.resize((double)pageHeight / (double)watermark.height()); | ||||
|   } | ||||
| 
 | ||||
|   int x = 0, y = 0; | ||||
|   switch (gravity) { | ||||
|     case 1: | ||||
|       break; | ||||
|     case 2: | ||||
|       x = (width / 2) - (watermark.width() / 2); | ||||
|       break; | ||||
|     case 3: | ||||
|       x = width - watermark.width(); | ||||
|       break; | ||||
|     case 5: | ||||
|       x = (width / 2) - (watermark.width() / 2); | ||||
|       y = (pageHeight / 2) - (watermark.height() / 2); | ||||
|       break; | ||||
|     case 6: | ||||
|       x = width - watermark.width(); | ||||
|       y = (pageHeight / 2) - (watermark.height() / 2); | ||||
|       break; | ||||
|     case 8: | ||||
|       x = (width / 2) - (watermark.width() / 2); | ||||
|       y = pageHeight - watermark.height(); | ||||
|       break; | ||||
|     case 9: | ||||
|       x = width - watermark.width(); | ||||
|       y = pageHeight - watermark.height(); | ||||
|       break; | ||||
|   } | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   int addedHeight = 0; | ||||
|   VImage contentAlpha; | ||||
|   VImage frameAlpha; | ||||
|   VImage bg; | ||||
|   VImage frame; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     if (append) { | ||||
|       VImage appended = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, | ||||
|                                        VImage::option()->set("expand", true)); | ||||
|       addedHeight = watermark.height(); | ||||
|       img.push_back(appended); | ||||
|     } else if (mc) { | ||||
|       VImage padded = | ||||
|           img_frame.embed(0, 0, width, pageHeight + 15, | ||||
|                           VImage::option()->set("background", 0xffffff)); | ||||
|       VImage composited = | ||||
|           padded.composite2(watermark, VIPS_BLEND_MODE_OVER, | ||||
|                             VImage::option() | ||||
|                                 ->set("x", width - 190) | ||||
|                                 ->set("y", padded.height() - 22)); | ||||
|       addedHeight = 15; | ||||
|       img.push_back(composited); | ||||
|     } else { | ||||
|       VImage composited; | ||||
|       if (alpha) { | ||||
|         if (i == 0) { | ||||
|           contentAlpha = watermark.extract_band(0).embed( | ||||
|               x, y, width, pageHeight, | ||||
|               VImage::option()->set("extend", "white")); | ||||
|           frameAlpha = watermark.extract_band(1).embed( | ||||
|               x, y, width, pageHeight, | ||||
|               VImage::option()->set("extend", "black")); | ||||
|           bg = frameAlpha.new_from_image({0, 0, 0}).copy(VImage::option()->set( | ||||
|               "interpretation", VIPS_INTERPRETATION_sRGB)); | ||||
|           frame = bg.bandjoin(frameAlpha); | ||||
|           if (*outType == "jpg" || *outType == "jpeg") { | ||||
|             *outType = "png"; | ||||
|           } | ||||
|         } | ||||
|         VImage content = | ||||
|             img_frame.extract_band(0, VImage::option()->set("n", 3)) | ||||
|                 .bandjoin(contentAlpha & img_frame.extract_band(3)); | ||||
| 
 | ||||
|         composited = | ||||
|             content.composite2(frame, VIPS_BLEND_MODE_OVER, | ||||
|                                VImage::option()->set("x", x)->set("y", y)); | ||||
|       } else { | ||||
|         composited = | ||||
|             img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER, | ||||
|                                  VImage::option()->set("x", x)->set("y", y)); | ||||
|       } | ||||
|       img.push_back(composited); | ||||
|     } | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight + addedHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(final.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Watermark(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Watermark(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|                 ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,86 +1,91 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Whisper(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int size = width / 6; | ||||
|   int dividedWidth = width / 175; | ||||
|   int rad = 1; | ||||
| 
 | ||||
|   string font_string = "Upright, Twemoji Color Font " + to_string(size); | ||||
| 
 | ||||
|   VImage mask; | ||||
|   if (dividedWidth >= 1) { | ||||
|     mask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128; | ||||
|     mask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth, | ||||
|                      VImage::option()->set("fill", true)); | ||||
|   } else { | ||||
|     mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128; | ||||
|     mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true)); | ||||
|   } | ||||
| 
 | ||||
|   VImage textIn = VImage::text( | ||||
|       ".", VImage::option()->set( | ||||
|                "fontfile", (basePath + "assets/fonts/whisper.otf").c_str())); | ||||
|   textIn = VImage::text( | ||||
|       ("<span foreground=\"white\">" + caption + "</span>").c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", width)); | ||||
| 
 | ||||
|   textIn = textIn.embed(rad + 10, rad + 10, (textIn.width() + 2 * rad) + 20, | ||||
|                         (textIn.height() + 2 * rad) + 20); | ||||
| 
 | ||||
|   VImage outline = textIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) | ||||
|                        .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); | ||||
|   outline = (outline == (vector<double>){0, 0, 0, 0}); | ||||
|   VImage invert = outline.extract_band(3).invert(); | ||||
|   outline = | ||||
|       outline.extract_band(0, VImage::option()->set("n", outline.bands() - 1)) | ||||
|           .bandjoin(invert); | ||||
|   VImage textImg = outline.composite2(textIn, VIPS_BLEND_MODE_OVER); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     img_frame = img_frame.composite2( | ||||
|         textImg, VIPS_BLEND_MODE_OVER, | ||||
|         VImage::option() | ||||
|             ->set("x", (width / 2) - (textImg.width() / 2)) | ||||
|             ->set("y", (pageHeight / 2) - (textImg.height() / 2))); | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Whisper(string type, string *outType, char *BufferData, | ||||
|               size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string caption = GetArgument<string>(Arguments, "caption"); | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
|   int size = width / 6; | ||||
|   int dividedWidth = width / 175; | ||||
|   int rad = 1; | ||||
| 
 | ||||
|   string font_string = "Upright, Twemoji Color Font " + to_string(size); | ||||
| 
 | ||||
|   VImage mask; | ||||
|   if (dividedWidth >= 1) { | ||||
|     mask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128; | ||||
|     mask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth, | ||||
|                      VImage::option()->set("fill", true)); | ||||
|   } else { | ||||
|     mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128; | ||||
|     mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true)); | ||||
|   } | ||||
| 
 | ||||
|   VImage textIn = VImage::text( | ||||
|       ".", VImage::option()->set( | ||||
|                "fontfile", (basePath + "assets/fonts/whisper.otf").c_str())); | ||||
|   textIn = VImage::text( | ||||
|       ("<span foreground=\"white\">" + caption + "</span>").c_str(), | ||||
|       VImage::option() | ||||
|           ->set("rgba", true) | ||||
|           ->set("align", VIPS_ALIGN_CENTRE) | ||||
|           ->set("font", font_string.c_str()) | ||||
|           ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) | ||||
|           ->set("width", width)); | ||||
| 
 | ||||
|   textIn = textIn.embed(rad + 10, rad + 10, (textIn.width() + 2 * rad) + 20, | ||||
|                         (textIn.height() + 2 * rad) + 20); | ||||
| 
 | ||||
|   VImage outline = textIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE) | ||||
|                        .gaussblur(0.5, VImage::option()->set("min_ampl", 0.1)); | ||||
|   outline = (outline == (vector<double>){0, 0, 0, 0}); | ||||
|   VImage invert = outline.extract_band(3).invert(); | ||||
|   outline = | ||||
|       outline.extract_band(0, VImage::option()->set("n", outline.bands() - 1)) | ||||
|           .bandjoin(invert); | ||||
|   VImage textImg = outline.composite2(textIn, VIPS_BLEND_MODE_OVER); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     img_frame = img_frame.composite2( | ||||
|         textImg, VIPS_BLEND_MODE_OVER, | ||||
|         VImage::option() | ||||
|             ->set("x", (width / 2) - (textImg.width() / 2)) | ||||
|             ->set("y", (pageHeight / 2) - (textImg.height() / 2))); | ||||
|     img.push_back(img_frame); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, pageHeight); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer( | ||||
|       ("." + *outType).c_str(), &buf, DataSize, | ||||
|       *outType == "gif" | ||||
|           ? VImage::option()->set("dither", 0)->set("reoptimise", 1) | ||||
|           : 0); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = width; | ||||
|   output["height"] = pageHeight; | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Whisper(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Whisper(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|               ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -1,47 +1,52 @@ | |||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| char *Zamn(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/zamn.png"; | ||||
|   VImage tmpl = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage composited = tmpl.insert( | ||||
|         img_frame.extract_band(0, VImage::option()->set("n", 3)) | ||||
|             .bandjoin(255) | ||||
|             .resize( | ||||
|                 303.0 / (double)width, | ||||
|                 VImage::option()->set("vscale", 438.0 / (double)pageHeight)), | ||||
|         310, 76); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 516); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   return (char *)buf; | ||||
| #include <vips/vips8> | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace vips; | ||||
| 
 | ||||
| ArgumentMap Zamn(string type, string *outType, char *BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t *DataSize) { | ||||
|   string basePath = GetArgument<string>(Arguments, "basePath"); | ||||
| 
 | ||||
|   VOption *options = VImage::option()->set("access", "sequential"); | ||||
| 
 | ||||
|   VImage in = | ||||
|       VImage::new_from_buffer(BufferData, BufferLength, "", | ||||
|                               type == "gif" ? options->set("n", -1) : options) | ||||
|           .colourspace(VIPS_INTERPRETATION_sRGB); | ||||
|   if (!in.has_alpha()) in = in.bandjoin(255); | ||||
| 
 | ||||
|   int width = in.width(); | ||||
|   int pageHeight = vips_image_get_page_height(in.get_image()); | ||||
|   int nPages = vips_image_get_n_pages(in.get_image()); | ||||
| 
 | ||||
|   string assetPath = basePath + "assets/images/zamn.png"; | ||||
|   VImage tmpl = VImage::new_from_file(assetPath.c_str()); | ||||
| 
 | ||||
|   vector<VImage> img; | ||||
|   for (int i = 0; i < nPages; i++) { | ||||
|     VImage img_frame = | ||||
|         type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; | ||||
|     VImage composited = tmpl.insert( | ||||
|         img_frame.extract_band(0, VImage::option()->set("n", 3)) | ||||
|             .bandjoin(255) | ||||
|             .resize( | ||||
|                 303.0 / (double)width, | ||||
|                 VImage::option()->set("vscale", 438.0 / (double)pageHeight)), | ||||
|         310, 76); | ||||
|     img.push_back(composited); | ||||
|   } | ||||
|   VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); | ||||
|   final.set(VIPS_META_PAGE_HEIGHT, 516); | ||||
| 
 | ||||
|   void *buf; | ||||
|   final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); | ||||
| 
 | ||||
|   ArgumentMap output; | ||||
|   output["buf"] = (char *)buf; | ||||
|   output["width"] = final.width(); | ||||
|   output["height"] = vips_image_get_page_height(final.get_image()); | ||||
| 
 | ||||
|   return output; | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| char* Zamn(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common.h" | ||||
| 
 | ||||
| using std::string; | ||||
| 
 | ||||
| ArgumentMap Zamn(string type, string* outType, char* BufferData, size_t BufferLength, | ||||
|            ArgumentMap Arguments, size_t* DataSize); | ||||
|  | @ -50,7 +50,9 @@ export default function run(object) { | |||
|         const result = img.image(object.cmd, objectWithFixedType); | ||||
|         const returnObject = { | ||||
|           buffer: result.data, | ||||
|           fileExtension: result.type | ||||
|           fileExtension: result.type, | ||||
|           width: result.width, | ||||
|           height: result.height | ||||
|         }; | ||||
|         resolve(returnObject); | ||||
|       } catch (e) { | ||||
|  |  | |||
|  | @ -135,7 +135,9 @@ function waitForWorker(worker) { | |||
|     worker.once("message", (data) => { | ||||
|       resolve({ | ||||
|         buffer: Buffer.from([...data.buffer]), | ||||
|         type: data.fileExtension | ||||
|         type: data.fileExtension, | ||||
|         width: data.width, | ||||
|         height: data.height | ||||
|       }); | ||||
|     }); | ||||
|     worker.once("error", reject); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue