Brunobkr/llama.cpp_AlgMor24_github
ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.
03k
1/*!2 Copyright 2013 Lovell Fuller and others.3 SPDX-License-Identifier: Apache-2.04*/5 6#include <algorithm>7#include <cstdlib>8#include <map>9#include <mutex>10#include <queue>11#include <string>12#include <tuple>13#include <utility>14#include <vector>15 16#include <napi.h>17#include <vips/vips8>18 19#include "./common.h"20 21using vips::VImage;22 23namespace sharp {24 25 // Convenience methods to access the attributes of a Napi::Object26 bool HasAttr(Napi::Object obj, std::string attr) {27 return obj.Has(attr);28 }29 std::string AttrAsStr(Napi::Object obj, std::string attr) {30 return obj.Get(attr).As<Napi::String>();31 }32 std::string AttrAsStr(Napi::Object obj, unsigned int const attr) {33 return obj.Get(attr).As<Napi::String>();34 }35 uint32_t AttrAsUint32(Napi::Object obj, std::string attr) {36 return obj.Get(attr).As<Napi::Number>().Uint32Value();37 }38 int32_t AttrAsInt32(Napi::Object obj, std::string attr) {39 return obj.Get(attr).As<Napi::Number>().Int32Value();40 }41 int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr) {42 return obj.Get(attr).As<Napi::Number>().Int32Value();43 }44 int64_t AttrAsInt64(Napi::Object obj, std::string attr) {45 return obj.Get(attr).As<Napi::Number>().Int64Value();46 }47 double AttrAsDouble(Napi::Object obj, std::string attr) {48 return obj.Get(attr).As<Napi::Number>().DoubleValue();49 }50 double AttrAsDouble(Napi::Object obj, unsigned int const attr) {51 return obj.Get(attr).As<Napi::Number>().DoubleValue();52 }53 bool AttrAsBool(Napi::Object obj, std::string attr) {54 return obj.Get(attr).As<Napi::Boolean>().Value();55 }56 std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr) {57 Napi::Array napiArray = obj.Get(attr).As<Napi::Array>();58 std::vector<double> vectorOfDouble(napiArray.Length());59 for (unsigned int i = 0; i < napiArray.Length(); i++) {60 vectorOfDouble[i] = AttrAsDouble(napiArray, i);61 }62 return vectorOfDouble;63 }64 std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr) {65 Napi::Array array = obj.Get(attr).As<Napi::Array>();66 std::vector<int32_t> vector(array.Length());67 for (unsigned int i = 0; i < array.Length(); i++) {68 vector[i] = AttrAsInt32(array, i);69 }70 return vector;71 }72 73 // Create an InputDescriptor instance from a Napi::Object describing an input image74 InputDescriptor* CreateInputDescriptor(Napi::Object input) {75 InputDescriptor *descriptor = new InputDescriptor;76 if (HasAttr(input, "file")) {77 descriptor->file = AttrAsStr(input, "file");78 } else if (HasAttr(input, "buffer")) {79 Napi::Buffer<char> buffer = input.Get("buffer").As<Napi::Buffer<char>>();80 descriptor->bufferLength = buffer.Length();81 descriptor->buffer = buffer.Data();82 descriptor->isBuffer = true;83 }84 descriptor->failOn = AttrAsEnum<VipsFailOn>(input, "failOn", VIPS_TYPE_FAIL_ON);85 // Density for vector-based input86 if (HasAttr(input, "density")) {87 descriptor->density = AttrAsDouble(input, "density");88 }89 // Should we ignore any embedded ICC profile90 if (HasAttr(input, "ignoreIcc")) {91 descriptor->ignoreIcc = AttrAsBool(input, "ignoreIcc");92 }93 // Raw pixel input94 if (HasAttr(input, "rawChannels")) {95 descriptor->rawDepth = AttrAsEnum<VipsBandFormat>(input, "rawDepth", VIPS_TYPE_BAND_FORMAT);96 descriptor->rawChannels = AttrAsUint32(input, "rawChannels");97 descriptor->rawWidth = AttrAsUint32(input, "rawWidth");98 descriptor->rawHeight = AttrAsUint32(input, "rawHeight");99 descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");100 descriptor->rawPageHeight = AttrAsUint32(input, "rawPageHeight");101 }102 // Multi-page input (GIF, TIFF, PDF)103 if (HasAttr(input, "pages")) {104 descriptor->pages = AttrAsInt32(input, "pages");105 }106 if (HasAttr(input, "page")) {107 descriptor->page = AttrAsUint32(input, "page");108 }109 // SVG110 if (HasAttr(input, "svgStylesheet")) {111 descriptor->svgStylesheet = AttrAsStr(input, "svgStylesheet");112 }113 if (HasAttr(input, "svgHighBitdepth")) {114 descriptor->svgHighBitdepth = AttrAsBool(input, "svgHighBitdepth");115 }116 // Multi-level input (OpenSlide)117 if (HasAttr(input, "openSlideLevel")) {118 descriptor->openSlideLevel = AttrAsUint32(input, "openSlideLevel");119 }120 // subIFD (OME-TIFF)121 if (HasAttr(input, "subifd")) {122 descriptor->tiffSubifd = AttrAsInt32(input, "tiffSubifd");123 }124 // // PDF background color125 if (HasAttr(input, "pdfBackground")) {126 descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground");127 }128 // Use JPEG 2000 oneshot mode?129 if (HasAttr(input, "jp2Oneshot")) {130 descriptor->jp2Oneshot = AttrAsBool(input, "jp2Oneshot");131 }132 // Create new image133 if (HasAttr(input, "createChannels")) {134 descriptor->createChannels = AttrAsUint32(input, "createChannels");135 descriptor->createWidth = AttrAsUint32(input, "createWidth");136 descriptor->createHeight = AttrAsUint32(input, "createHeight");137 descriptor->createPageHeight = AttrAsUint32(input, "createPageHeight");138 if (HasAttr(input, "createNoiseType")) {139 descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");140 descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");141 descriptor->createNoiseSigma = AttrAsDouble(input, "createNoiseSigma");142 } else {143 descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");144 }145 }146 // Create new image with text147 if (HasAttr(input, "textValue")) {148 descriptor->textValue = AttrAsStr(input, "textValue");149 if (HasAttr(input, "textFont")) {150 descriptor->textFont = AttrAsStr(input, "textFont");151 }152 if (HasAttr(input, "textFontfile")) {153 descriptor->textFontfile = AttrAsStr(input, "textFontfile");154 }155 if (HasAttr(input, "textWidth")) {156 descriptor->textWidth = AttrAsUint32(input, "textWidth");157 }158 if (HasAttr(input, "textHeight")) {159 descriptor->textHeight = AttrAsUint32(input, "textHeight");160 }161 if (HasAttr(input, "textAlign")) {162 descriptor->textAlign = AttrAsEnum<VipsAlign>(input, "textAlign", VIPS_TYPE_ALIGN);163 }164 if (HasAttr(input, "textJustify")) {165 descriptor->textJustify = AttrAsBool(input, "textJustify");166 }167 if (HasAttr(input, "textDpi")) {168 descriptor->textDpi = AttrAsUint32(input, "textDpi");169 }170 if (HasAttr(input, "textRgba")) {171 descriptor->textRgba = AttrAsBool(input, "textRgba");172 }173 if (HasAttr(input, "textSpacing")) {174 descriptor->textSpacing = AttrAsUint32(input, "textSpacing");175 }176 if (HasAttr(input, "textWrap")) {177 descriptor->textWrap = AttrAsEnum<VipsTextWrap>(input, "textWrap", VIPS_TYPE_TEXT_WRAP);178 }179 }180 // Join images together181 if (HasAttr(input, "joinAnimated")) {182 descriptor->joinAnimated = AttrAsBool(input, "joinAnimated");183 }184 if (HasAttr(input, "joinAcross")) {185 descriptor->joinAcross = AttrAsUint32(input, "joinAcross");186 }187 if (HasAttr(input, "joinShim")) {188 descriptor->joinShim = AttrAsUint32(input, "joinShim");189 }190 if (HasAttr(input, "joinBackground")) {191 descriptor->joinBackground = AttrAsVectorOfDouble(input, "joinBackground");192 }193 if (HasAttr(input, "joinHalign")) {194 descriptor->joinHalign = AttrAsEnum<VipsAlign>(input, "joinHalign", VIPS_TYPE_ALIGN);195 }196 if (HasAttr(input, "joinValign")) {197 descriptor->joinValign = AttrAsEnum<VipsAlign>(input, "joinValign", VIPS_TYPE_ALIGN);198 }199 descriptor->limitInputPixels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputPixels"));200 descriptor->limitInputChannels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputChannels"));201 if (HasAttr(input, "access")) {202 descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;203 }204 // Remove safety features and allow unlimited input205 descriptor->unlimited = AttrAsBool(input, "unlimited");206 // Use the EXIF orientation to auto orient the image207 descriptor->autoOrient = AttrAsBool(input, "autoOrient");208 return descriptor;209 }210 211 // How many tasks are in the queue?212 std::atomic<int> counterQueue{0};213 214 // How many tasks are being processed?215 std::atomic<int> counterProcess{0};216 217 // Filename extension checkers218 static bool EndsWith(std::string const &str, std::string const &end) {219 return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end);220 }221 bool IsJpeg(std::string const &str) {222 return EndsWith(str, ".jpg") || EndsWith(str, ".jpeg") || EndsWith(str, ".JPG") || EndsWith(str, ".JPEG");223 }224 bool IsPng(std::string const &str) {225 return EndsWith(str, ".png") || EndsWith(str, ".PNG");226 }227 bool IsWebp(std::string const &str) {228 return EndsWith(str, ".webp") || EndsWith(str, ".WEBP");229 }230 bool IsGif(std::string const &str) {231 return EndsWith(str, ".gif") || EndsWith(str, ".GIF");232 }233 bool IsJp2(std::string const &str) {234 return EndsWith(str, ".jp2") || EndsWith(str, ".jpx") || EndsWith(str, ".j2k") || EndsWith(str, ".j2c")235 || EndsWith(str, ".JP2") || EndsWith(str, ".JPX") || EndsWith(str, ".J2K") || EndsWith(str, ".J2C");236 }237 bool IsTiff(std::string const &str) {238 return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");239 }240 bool IsHeic(std::string const &str) {241 return EndsWith(str, ".heic") || EndsWith(str, ".HEIC");242 }243 bool IsHeif(std::string const &str) {244 return EndsWith(str, ".heif") || EndsWith(str, ".HEIF") || IsHeic(str) || IsAvif(str);245 }246 bool IsAvif(std::string const &str) {247 return EndsWith(str, ".avif") || EndsWith(str, ".AVIF");248 }249 bool IsJxl(std::string const &str) {250 return EndsWith(str, ".jxl") || EndsWith(str, ".JXL");251 }252 bool IsDz(std::string const &str) {253 return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");254 }255 bool IsDzZip(std::string const &str) {256 return EndsWith(str, ".zip") || EndsWith(str, ".ZIP") || EndsWith(str, ".szi") || EndsWith(str, ".SZI");257 }258 bool IsV(std::string const &str) {259 return EndsWith(str, ".v") || EndsWith(str, ".V") || EndsWith(str, ".vips") || EndsWith(str, ".VIPS");260 }261 262 /*263 Trim space from end of string.264 */265 std::string TrimEnd(std::string const &str) {266 return str.substr(0, str.find_last_not_of(" \n\r\f") + 1);267 }268 269 /*270 Provide a string identifier for the given image type.271 */272 std::string ImageTypeId(ImageType const imageType) {273 std::string id;274 switch (imageType) {275 case ImageType::JPEG: id = "jpeg"; break;276 case ImageType::PNG: id = "png"; break;277 case ImageType::WEBP: id = "webp"; break;278 case ImageType::TIFF: id = "tiff"; break;279 case ImageType::GIF: id = "gif"; break;280 case ImageType::JP2: id = "jp2"; break;281 case ImageType::SVG: id = "svg"; break;282 case ImageType::HEIF: id = "heif"; break;283 case ImageType::PDF: id = "pdf"; break;284 case ImageType::MAGICK: id = "magick"; break;285 case ImageType::OPENSLIDE: id = "openslide"; break;286 case ImageType::PPM: id = "ppm"; break;287 case ImageType::FITS: id = "fits"; break;288 case ImageType::EXR: id = "exr"; break;289 case ImageType::JXL: id = "jxl"; break;290 case ImageType::RAD: id = "rad"; break;291 case ImageType::DCRAW: id = "dcraw"; break;292 case ImageType::UHDR: id = "uhdr"; break;293 case ImageType::VIPS: id = "vips"; break;294 case ImageType::RAW: id = "raw"; break;295 case ImageType::UNKNOWN: id = "unknown"; break;296 case ImageType::MISSING: id = "missing"; break;297 }298 return id;299 }300 301 /**302 * Regenerate this table with something like:303 *304 * $ vips -l foreign | grep -i load | awk '{ print $2, $1; }'305 *306 * Plus a bit of editing.307 */308 std::map<std::string, ImageType> loaderToType = {309 { "VipsForeignLoadJpegFile", ImageType::JPEG },310 { "VipsForeignLoadJpegBuffer", ImageType::JPEG },311 { "VipsForeignLoadPngFile", ImageType::PNG },312 { "VipsForeignLoadPngBuffer", ImageType::PNG },313 { "VipsForeignLoadWebpFile", ImageType::WEBP },314 { "VipsForeignLoadWebpBuffer", ImageType::WEBP },315 { "VipsForeignLoadTiffFile", ImageType::TIFF },316 { "VipsForeignLoadTiffBuffer", ImageType::TIFF },317 { "VipsForeignLoadGifFile", ImageType::GIF },318 { "VipsForeignLoadGifBuffer", ImageType::GIF },319 { "VipsForeignLoadNsgifFile", ImageType::GIF },320 { "VipsForeignLoadNsgifBuffer", ImageType::GIF },321 { "VipsForeignLoadJp2kBuffer", ImageType::JP2 },322 { "VipsForeignLoadJp2kFile", ImageType::JP2 },323 { "VipsForeignLoadSvgFile", ImageType::SVG },324 { "VipsForeignLoadSvgBuffer", ImageType::SVG },325 { "VipsForeignLoadHeifFile", ImageType::HEIF },326 { "VipsForeignLoadHeifBuffer", ImageType::HEIF },327 { "VipsForeignLoadPdfFile", ImageType::PDF },328 { "VipsForeignLoadPdfBuffer", ImageType::PDF },329 { "VipsForeignLoadMagickFile", ImageType::MAGICK },330 { "VipsForeignLoadMagickBuffer", ImageType::MAGICK },331 { "VipsForeignLoadMagick7File", ImageType::MAGICK },332 { "VipsForeignLoadMagick7Buffer", ImageType::MAGICK },333 { "VipsForeignLoadOpenslideFile", ImageType::OPENSLIDE },334 { "VipsForeignLoadPpmFile", ImageType::PPM },335 { "VipsForeignLoadFitsFile", ImageType::FITS },336 { "VipsForeignLoadOpenexr", ImageType::EXR },337 { "VipsForeignLoadJxlFile", ImageType::JXL },338 { "VipsForeignLoadJxlBuffer", ImageType::JXL },339 { "VipsForeignLoadRadFile", ImageType::RAD },340 { "VipsForeignLoadRadBuffer", ImageType::RAD },341 { "VipsForeignLoadDcRawFile", ImageType::DCRAW },342 { "VipsForeignLoadDcRawBuffer", ImageType::DCRAW },343 { "VipsForeignLoadUhdr", ImageType::UHDR },344 { "VipsForeignLoadUhdrFile", ImageType::UHDR },345 { "VipsForeignLoadUhdrBuffer", ImageType::UHDR },346 { "VipsForeignLoadVips", ImageType::VIPS },347 { "VipsForeignLoadVipsFile", ImageType::VIPS },348 { "VipsForeignLoadRaw", ImageType::RAW }349 };350 351 /*352 Determine image format of a buffer.353 */354 ImageType DetermineImageType(void *buffer, size_t const length) {355 ImageType imageType = ImageType::UNKNOWN;356 char const *load = vips_foreign_find_load_buffer(buffer, length);357 if (load != nullptr) {358 auto it = loaderToType.find(load);359 if (it != loaderToType.end()) {360 imageType = it->second;361 }362 }363 if (imageType == ImageType::UHDR) {364 imageType = ImageType::JPEG;365 }366 return imageType;367 }368 369 /*370 Determine image format, reads the first few bytes of the file371 */372 ImageType DetermineImageType(char const *file) {373 ImageType imageType = ImageType::UNKNOWN;374 char const *load = vips_foreign_find_load(file);375 if (load != nullptr) {376 auto it = loaderToType.find(load);377 if (it != loaderToType.end()) {378 imageType = it->second;379 }380 } else {381 if (EndsWith(vips::VError().what(), " does not exist\n")) {382 imageType = ImageType::MISSING;383 }384 }385 if (imageType == ImageType::UHDR) {386 imageType = ImageType::JPEG;387 }388 return imageType;389 }390 391 /*392 Does this image type support multiple pages?393 */394 bool ImageTypeSupportsPage(ImageType imageType) {395 return396 imageType == ImageType::WEBP ||397 imageType == ImageType::MAGICK ||398 imageType == ImageType::GIF ||399 imageType == ImageType::JP2 ||400 imageType == ImageType::TIFF ||401 imageType == ImageType::HEIF ||402 imageType == ImageType::PDF;403 }404 405 /*406 Does this image type support removal of safety limits?407 */408 bool ImageTypeSupportsUnlimited(ImageType imageType) {409 return410 imageType == ImageType::JPEG ||411 imageType == ImageType::PNG ||412 imageType == ImageType::SVG ||413 imageType == ImageType::TIFF ||414 imageType == ImageType::HEIF;415 }416 417 /*418 Format-specific options builder419 */420 vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor) {421 vips::VOption *option = VImage::option()422 ->set("access", descriptor->access)423 ->set("fail_on", descriptor->failOn);424 if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {425 option->set("unlimited", true);426 }427 if (ImageTypeSupportsPage(imageType)) {428 option->set("n", descriptor->pages);429 option->set("page", std::max(0, descriptor->page));430 }431 switch (imageType) {432 case ImageType::SVG:433 option->set("dpi", descriptor->density)434 ->set("stylesheet", descriptor->svgStylesheet.data())435 ->set("high_bitdepth", descriptor->svgHighBitdepth);436 break;437 case ImageType::TIFF:438 option->set("subifd", descriptor->tiffSubifd);439 break;440 case ImageType::PDF:441 option->set("dpi", descriptor->density)442 ->set("background", descriptor->pdfBackground);443 break;444 case ImageType::OPENSLIDE:445 option->set("level", descriptor->openSlideLevel);446 break;447 case ImageType::JP2:448 option->set("oneshot", descriptor->jp2Oneshot);449 break;450 case ImageType::MAGICK:451 option->set("density", std::to_string(descriptor->density).data());452 break;453 default:454 break;455 }456 return option;457 }458 459 /*460 Should HEIF image be re-opened using the primary item?461 */462 static bool HeifPrimaryPageReopen(VImage image, InputDescriptor *descriptor) {463 if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT && image.get_typeof("heif-primary") == G_TYPE_INT) {464 if (image.get_int(VIPS_META_N_PAGES) > 1 && descriptor->pages == 1 && descriptor->page == -1) {465 int const pagePrimary = image.get_int("heif-primary");466 if (pagePrimary != 0) {467 descriptor->page = pagePrimary;468 return true;469 }470 }471 }472 return false;473 }474 475 /*476 Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)477 */478 std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor) {479 VImage image;480 ImageType imageType;481 if (descriptor->isBuffer) {482 if (descriptor->rawChannels > 0) {483 // Raw, uncompressed pixel data484 bool const is8bit = vips_band_format_is8bit(descriptor->rawDepth);485 image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,486 descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, descriptor->rawDepth);487 if (descriptor->rawChannels < 3) {488 image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_GREY16;489 } else {490 image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;491 }492 if (descriptor->rawPageHeight > 0) {493 image.set(VIPS_META_PAGE_HEIGHT, descriptor->rawPageHeight);494 image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->rawHeight / descriptor->rawPageHeight));495 }496 if (descriptor->rawPremultiplied) {497 image = image.unpremultiply();498 }499 imageType = ImageType::RAW;500 } else {501 // Compressed data502 imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);503 if (imageType != ImageType::UNKNOWN) {504 try {505 vips::VOption *option = GetOptionsForImageType(imageType, descriptor);506 image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);507 if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {508 image = SetDensity(image, descriptor->density);509 } else if (imageType == ImageType::HEIF && HeifPrimaryPageReopen(image, descriptor)) {510 option = GetOptionsForImageType(imageType, descriptor);511 image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);512 }513 } catch (std::runtime_error const &err) {514 throw std::runtime_error(std::string("Input buffer has corrupt header: ") + err.what());515 }516 } else {517 throw std::runtime_error("Input buffer contains unsupported image format");518 }519 }520 } else {521 int const channels = descriptor->createChannels;522 if (channels > 0) {523 // Create new image524 if (descriptor->createNoiseType == "gaussian") {525 std::vector<VImage> bands = {};526 bands.reserve(channels);527 for (int _band = 0; _band < channels; _band++) {528 bands.push_back(VImage::gaussnoise(descriptor->createWidth, descriptor->createHeight, VImage::option()529 ->set("mean", descriptor->createNoiseMean)530 ->set("sigma", descriptor->createNoiseSigma)));531 }532 image = VImage::bandjoin(bands).copy(VImage::option()->set("interpretation",533 channels < 3 ? VIPS_INTERPRETATION_B_W: VIPS_INTERPRETATION_sRGB));534 } else {535 std::vector<double> background = {536 descriptor->createBackground[0],537 descriptor->createBackground[1],538 descriptor->createBackground[2]539 };540 if (channels == 4) {541 background.push_back(descriptor->createBackground[3]);542 }543 image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight)544 .copy(VImage::option()->set("interpretation",545 channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB))546 .new_from_image(background);547 }548 if (descriptor->createPageHeight > 0) {549 image.set(VIPS_META_PAGE_HEIGHT, descriptor->createPageHeight);550 image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->createHeight / descriptor->createPageHeight));551 }552 image = image.cast(VIPS_FORMAT_UCHAR);553 imageType = ImageType::RAW;554 } else if (descriptor->textValue.length() > 0) {555 // Create a new image with text556 vips::VOption *textOptions = VImage::option()557 ->set("align", descriptor->textAlign)558 ->set("justify", descriptor->textJustify)559 ->set("rgba", descriptor->textRgba)560 ->set("spacing", descriptor->textSpacing)561 ->set("wrap", descriptor->textWrap)562 ->set("autofit_dpi", &descriptor->textAutofitDpi);563 if (descriptor->textWidth > 0) {564 textOptions->set("width", descriptor->textWidth);565 }566 // Ignore dpi if height is set567 if (descriptor->textWidth > 0 && descriptor->textHeight > 0) {568 textOptions->set("height", descriptor->textHeight);569 } else if (descriptor->textDpi > 0) {570 textOptions->set("dpi", descriptor->textDpi);571 }572 if (descriptor->textFont.length() > 0) {573 textOptions->set("font", const_cast<char*>(descriptor->textFont.data()));574 }575 if (descriptor->textFontfile.length() > 0) {576 textOptions->set("fontfile", const_cast<char*>(descriptor->textFontfile.data()));577 }578 image = VImage::text(const_cast<char *>(descriptor->textValue.data()), textOptions);579 if (!descriptor->textRgba) {580 image = image.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_B_W));581 }582 imageType = ImageType::RAW;583 } else {584 // From filesystem585 imageType = DetermineImageType(descriptor->file.data());586 if (imageType == ImageType::MISSING) {587 if (descriptor->file.find("<svg") != std::string::npos) {588 throw std::runtime_error("Input file is missing, did you mean "589 "sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");590 }591 throw std::runtime_error("Input file is missing: " + descriptor->file);592 }593 if (imageType != ImageType::UNKNOWN) {594 try {595 vips::VOption *option = GetOptionsForImageType(imageType, descriptor);596 image = VImage::new_from_file(descriptor->file.data(), option);597 if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {598 image = SetDensity(image, descriptor->density);599 } else if (imageType == ImageType::HEIF && HeifPrimaryPageReopen(image, descriptor)) {600 option = GetOptionsForImageType(imageType, descriptor);601 image = VImage::new_from_file(descriptor->file.data(), option);602 }603 } catch (std::runtime_error const &err) {604 throw std::runtime_error(std::string("Input file has corrupt header: ") + err.what());605 }606 } else {607 throw std::runtime_error("Input file contains unsupported image format");608 }609 }610 }611 612 // Limit input images to a given number of pixels, where pixels = width * height613 if (descriptor->limitInputPixels > 0 &&614 static_cast<uint64_t>(image.width()) * image.height() > descriptor->limitInputPixels) {615 throw std::runtime_error("Input image exceeds pixel limit");616 }617 if (descriptor->limitInputChannels > 0 && static_cast<uint64_t>(image.bands()) > descriptor->limitInputChannels) {618 throw std::runtime_error("Input image exceeds channel limit");619 }620 return std::make_tuple(image, imageType);621 }622 623 /*624 Does this image have an embedded profile?625 */626 bool HasProfile(VImage image) {627 return image.get_typeof(VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB;628 }629 630 /*631 Get copy of embedded profile.632 */633 std::pair<char*, size_t> GetProfile(VImage image) {634 std::pair<char*, size_t> icc(nullptr, 0);635 if (HasProfile(image)) {636 size_t length;637 const void *data = image.get_blob(VIPS_META_ICC_NAME, &length);638 icc.first = static_cast<char*>(vips_malloc(reinterpret_cast<VipsObject*>(image.get_image()), length));639 icc.second = length;640 memcpy(icc.first, data, length);641 }642 return icc;643 }644 645 /*646 Set embedded profile.647 */648 VImage SetProfile(VImage image, std::pair<char*, size_t> icc) {649 if (icc.first != nullptr) {650 image = image.copy();651 image.set(VIPS_META_ICC_NAME, nullptr, icc.first, icc.second);652 }653 return image;654 }655 656 static void* RemoveExifCallback(VipsImage *image, char const *field, GValue *value, void *data) {657 std::vector<std::string> *fieldNames = static_cast<std::vector<std::string> *>(data);658 std::string fieldName(field);659 if (fieldName.substr(0, 8) == ("exif-ifd")) {660 fieldNames->push_back(fieldName);661 }662 return nullptr;663 }664 665 /*666 Remove all EXIF-related image fields.667 */668 VImage RemoveExif(VImage image) {669 std::vector<std::string> fieldNames;670 vips_image_map(image.get_image(), static_cast<VipsImageMapFn>(RemoveExifCallback), &fieldNames);671 for (const auto& f : fieldNames) {672 image.remove(f.data());673 }674 return image;675 }676 677 /*678 Get EXIF Orientation of image, if any.679 */680 int ExifOrientation(VImage image) {681 int orientation = 0;682 if (image.get_typeof(VIPS_META_ORIENTATION) != 0) {683 orientation = image.get_int(VIPS_META_ORIENTATION);684 }685 return orientation;686 }687 688 /*689 Set EXIF Orientation of image.690 */691 VImage SetExifOrientation(VImage image, int const orientation) {692 VImage copy = image.copy();693 copy.set(VIPS_META_ORIENTATION, orientation);694 return copy;695 }696 697 /*698 Remove EXIF Orientation from image.699 */700 VImage RemoveExifOrientation(VImage image) {701 VImage copy = image.copy();702 copy.remove(VIPS_META_ORIENTATION);703 copy.remove("exif-ifd0-Orientation");704 return copy;705 }706 707 /*708 Set animation properties if necessary.709 */710 VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) {711 bool hasDelay = !delay.empty();712 VImage copy = image.copy();713 714 // Only set page-height if we have more than one page, or this could715 // accidentally turn into an animated image later.716 if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);717 if (hasDelay) {718 if (delay.size() == 1) {719 // We have just one delay, repeat that value for all frames.720 delay.insert(delay.end(), nPages - 1, delay[0]);721 }722 copy.set("delay", delay);723 }724 if (nPages == 1 && !hasDelay && loop == -1) {725 loop = 1;726 }727 if (loop != -1) copy.set("loop", loop);728 729 return copy;730 }731 732 /*733 Remove animation properties from image.734 */735 VImage RemoveAnimationProperties(VImage image) {736 VImage copy = image.copy();737 copy.remove(VIPS_META_PAGE_HEIGHT);738 copy.remove("delay");739 copy.remove("loop");740 return copy;741 }742 743 /*744 Remove GIF palette from image.745 */746 VImage RemoveGifPalette(VImage image) {747 VImage copy = image.copy();748 copy.remove("gif-palette");749 return copy;750 }751 752 /*753 Does this image have a non-default density?754 */755 bool HasDensity(VImage image) {756 return image.xres() > 1.0;757 }758 759 /*760 Get pixels/mm resolution as pixels/inch density.761 */762 int GetDensity(VImage image) {763 return static_cast<int>(round(image.xres() * 25.4));764 }765 766 /*767 Set pixels/mm resolution based on a pixels/inch density.768 */769 VImage SetDensity(VImage image, const double density) {770 const double pixelsPerMm = density / 25.4;771 VImage copy = image.copy();772 copy.get_image()->Xres = pixelsPerMm;773 copy.get_image()->Yres = pixelsPerMm;774 return copy;775 }776 777 /*778 Multi-page images can have a page height. Fetch it, and sanity check it.779 If page-height is not set, it defaults to the image height780 */781 int GetPageHeight(VImage image) {782 return vips_image_get_page_height(image.get_image());783 }784 785 /*786 Check the proposed format supports the current dimensions.787 */788 void AssertImageTypeDimensions(VImage image, ImageType const imageType) {789 const int height = image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT790 ? image.get_int(VIPS_META_PAGE_HEIGHT)791 : image.height();792 if (imageType == ImageType::JPEG) {793 if (image.width() > 65535 || height > 65535) {794 throw std::runtime_error("Processed image is too large for the JPEG format");795 }796 } else if (imageType == ImageType::WEBP) {797 if (image.width() > 16383 || height > 16383) {798 throw std::runtime_error("Processed image is too large for the WebP format");799 }800 } else if (imageType == ImageType::GIF) {801 if (image.width() > 65535 || height > 65535) {802 throw std::runtime_error("Processed image is too large for the GIF format");803 }804 } else if (imageType == ImageType::HEIF) {805 if (image.width() > 16384 || height > 16384) {806 throw std::runtime_error("Processed image is too large for the HEIF format");807 }808 }809 }810 811 /*812 Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows813 */814 std::function<void(void*, char*)> FreeCallback = [](void*, char* data) {815 g_free(data);816 };817 818 /*819 Temporary buffer of warnings820 */821 std::queue<std::string> vipsWarnings;822 std::mutex vipsWarningsMutex;823 824 /*825 Called with warnings from the glib-registered "VIPS" domain826 */827 void VipsWarningCallback(char const* log_domain, GLogLevelFlags log_level, char const* message, void* ignore) {828 std::lock_guard<std::mutex> lock(vipsWarningsMutex);829 vipsWarnings.emplace(message);830 }831 832 /*833 Pop the oldest warning message from the queue834 */835 std::string VipsWarningPop() {836 std::string warning;837 std::lock_guard<std::mutex> lock(vipsWarningsMutex);838 if (!vipsWarnings.empty()) {839 warning = vipsWarnings.front();840 vipsWarnings.pop();841 }842 return warning;843 }844 845 /*846 Attach an event listener for progress updates, used to detect timeout847 */848 void SetTimeout(VImage image, int const seconds) {849 if (seconds > 0) {850 VipsImage *im = image.get_image();851 if (im->progress_signal == NULL) {852 int *timeout = VIPS_NEW(im, int);853 *timeout = seconds;854 g_signal_connect(im, "eval", G_CALLBACK(VipsProgressCallBack), timeout);855 vips_image_set_progress(im, true);856 }857 }858 }859 860 /*861 Event listener for progress updates, used to detect timeout862 */863 void VipsProgressCallBack(VipsImage *im, VipsProgress *progress, int *timeout) {864 if (*timeout > 0 && progress->run >= *timeout) {865 vips_image_set_kill(im, true);866 vips_error("timeout", "%d%% complete", progress->percent);867 *timeout = 0;868 }869 }870 871 /*872 Calculate the (left, top) coordinates of the output image873 within the input image, applying the given gravity during an embed.874 875 @Azurebyte: We are basically swapping the inWidth and outWidth, inHeight and outHeight from the CalculateCrop function.876 */877 std::tuple<int, int> CalculateEmbedPosition(int const inWidth, int const inHeight,878 int const outWidth, int const outHeight, int const gravity) {879 880 int left = 0;881 int top = 0;882 switch (gravity) {883 case 1:884 // North885 left = (outWidth - inWidth) / 2;886 break;887 case 2:888 // East889 left = outWidth - inWidth;890 top = (outHeight - inHeight) / 2;891 break;892 case 3:893 // South894 left = (outWidth - inWidth) / 2;895 top = outHeight - inHeight;896 break;897 case 4:898 // West899 top = (outHeight - inHeight) / 2;900 break;901 case 5:902 // Northeast903 left = outWidth - inWidth;904 break;905 case 6:906 // Southeast907 left = outWidth - inWidth;908 top = outHeight - inHeight;909 break;910 case 7:911 // Southwest912 top = outHeight - inHeight;913 break;914 case 8:915 // Northwest916 // Which is the default is 0,0 so we do not assign anything here.917 break;918 default:919 // Centre920 left = (outWidth - inWidth) / 2;921 top = (outHeight - inHeight) / 2;922 }923 return std::make_tuple(left, top);924 }925 926 /*927 Calculate the (left, top) coordinates of the output image928 within the input image, applying the given gravity during a crop.929 */930 std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,931 int const outWidth, int const outHeight, int const gravity) {932 933 int left = 0;934 int top = 0;935 switch (gravity) {936 case 1:937 // North938 left = (inWidth - outWidth + 1) / 2;939 break;940 case 2:941 // East942 left = inWidth - outWidth;943 top = (inHeight - outHeight + 1) / 2;944 break;945 case 3:946 // South947 left = (inWidth - outWidth + 1) / 2;948 top = inHeight - outHeight;949 break;950 case 4:951 // West952 top = (inHeight - outHeight + 1) / 2;953 break;954 case 5:955 // Northeast956 left = inWidth - outWidth;957 break;958 case 6:959 // Southeast960 left = inWidth - outWidth;961 top = inHeight - outHeight;962 break;963 case 7:964 // Southwest965 top = inHeight - outHeight;966 break;967 case 8:968 // Northwest969 break;970 default:971 // Centre972 left = (inWidth - outWidth + 1) / 2;973 top = (inHeight - outHeight + 1) / 2;974 }975 return std::make_tuple(left, top);976 }977 978 /*979 Calculate the (left, top) coordinates of the output image980 within the input image, applying the given x and y offsets.981 */982 std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,983 int const outWidth, int const outHeight, int const x, int const y) {984 985 // default values986 int left = 0;987 int top = 0;988 989 // assign only if valid990 if (x < (inWidth - outWidth)) {991 left = x;992 } else if (x >= (inWidth - outWidth)) {993 left = inWidth - outWidth;994 }995 996 if (y < (inHeight - outHeight)) {997 top = y;998 } else if (y >= (inHeight - outHeight)) {999 top = inHeight - outHeight;1000 }1001 1002 return std::make_tuple(left, top);1003 }1004 1005 /*1006 Are pixel values in this image 16-bit integer?1007 */1008 bool Is16Bit(VipsInterpretation const interpretation) {1009 return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;1010 }1011 1012 /*1013 Convert RGBA value to another colourspace1014 */1015 std::vector<double> GetRgbaAsColourspace(std::vector<double> const rgba,1016 VipsInterpretation const interpretation, bool premultiply) {1017 int const bands = static_cast<int>(rgba.size());1018 if (bands < 3) {1019 return rgba;1020 }1021 VImage pixel = VImage::new_matrix(1, 1);1022 pixel.set("bands", bands);1023 pixel = pixel1024 .new_from_image(rgba)1025 .colourspace(interpretation, VImage::option()->set("source_space", VIPS_INTERPRETATION_sRGB));1026 if (premultiply) {1027 pixel = pixel.premultiply();1028 }1029 return pixel(0, 0);1030 }1031 1032 /*1033 Apply the alpha channel to a given colour1034 */1035 std::tuple<VImage, std::vector<double>> ApplyAlpha(VImage image, std::vector<double> colour, bool premultiply) {1036 // Scale up 8-bit values to match 16-bit input image1037 double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;1038 // Create alphaColour colour1039 std::vector<double> alphaColour;1040 if (image.bands() > 2) {1041 alphaColour = {1042 multiplier * colour[0],1043 multiplier * colour[1],1044 multiplier * colour[2]1045 };1046 } else {1047 // Convert sRGB to greyscale1048 alphaColour = { multiplier * (1049 0.2126 * colour[0] +1050 0.7152 * colour[1] +1051 0.0722 * colour[2])1052 };1053 }1054 // Add alpha channel(s) to alphaColour colour1055 if (colour[3] < 255.0 || image.has_alpha()) {1056 int extraBands = image.bands() > 4 ? image.bands() - 3 : 1;1057 alphaColour.insert(alphaColour.end(), extraBands, colour[3] * multiplier);1058 }1059 // Ensure alphaColour colour uses correct colourspace1060 alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);1061 // Add non-transparent alpha channel, if required1062 if (colour[3] < 255.0 && !image.has_alpha()) {1063 image = image.bandjoin_const({ 255 * multiplier });1064 }1065 return std::make_tuple(image, alphaColour);1066 }1067 1068 /*1069 Removes alpha channels, if any.1070 */1071 VImage RemoveAlpha(VImage image) {1072 while (image.bands() > 1 && image.has_alpha()) {1073 image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));1074 }1075 return image;1076 }1077 1078 /*1079 Ensures alpha channel, if missing.1080 */1081 VImage EnsureAlpha(VImage image, double const value) {1082 if (!image.has_alpha()) {1083 image = image.bandjoin_const({ value * vips_interpretation_max_alpha(image.interpretation()) });1084 }1085 return image;1086 }1087 1088 std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,1089 Canvas canvas, bool withoutEnlargement, bool withoutReduction) {1090 double hshrink = 1.0;1091 double vshrink = 1.0;1092 1093 if (targetWidth > 0 && targetHeight > 0) {1094 // Fixed width and height1095 hshrink = static_cast<double>(width) / targetWidth;1096 vshrink = static_cast<double>(height) / targetHeight;1097 1098 switch (canvas) {1099 case Canvas::CROP:1100 case Canvas::MIN:1101 if (hshrink < vshrink) {1102 vshrink = hshrink;1103 } else {1104 hshrink = vshrink;1105 }1106 break;1107 case Canvas::EMBED:1108 case Canvas::MAX:1109 if (hshrink > vshrink) {1110 vshrink = hshrink;1111 } else {1112 hshrink = vshrink;1113 }1114 break;1115 case Canvas::IGNORE_ASPECT:1116 break;1117 }1118 } else if (targetWidth > 0) {1119 // Fixed width1120 hshrink = static_cast<double>(width) / targetWidth;1121 1122 if (canvas != Canvas::IGNORE_ASPECT) {1123 // Auto height1124 vshrink = hshrink;1125 }1126 } else if (targetHeight > 0) {1127 // Fixed height1128 vshrink = static_cast<double>(height) / targetHeight;1129 1130 if (canvas != Canvas::IGNORE_ASPECT) {1131 // Auto width1132 hshrink = vshrink;1133 }1134 }1135 1136 // We should not reduce or enlarge the output image, if1137 // withoutReduction or withoutEnlargement is specified.1138 if (withoutReduction) {1139 // Equivalent of VIPS_SIZE_UP1140 hshrink = std::min(1.0, hshrink);1141 vshrink = std::min(1.0, vshrink);1142 } else if (withoutEnlargement) {1143 // Equivalent of VIPS_SIZE_DOWN1144 hshrink = std::max(1.0, hshrink);1145 vshrink = std::max(1.0, vshrink);1146 }1147 1148 // We don't want to shrink so much that we send an axis to 01149 hshrink = std::min(hshrink, static_cast<double>(width));1150 vshrink = std::min(vshrink, static_cast<double>(height));1151 1152 return std::make_pair(hshrink, vshrink);1153 }1154 1155 /*1156 Ensure decoding remains sequential.1157 */1158 VImage StaySequential(VImage image, bool condition) {1159 if (vips_image_is_sequential(image.get_image()) && condition) {1160 image = image.copy_memory().copy();1161 image.remove(VIPS_META_SEQUENTIAL);1162 }1163 return image;1164 }1165 1166 /*1167 Does this image have a gain map?1168 */1169 bool HasGainMap(VImage image) {1170 return image.get_typeof("gainmap") == VIPS_TYPE_BLOB ||1171 image.get_typeof("gainmap-data") == VIPS_TYPE_BLOB;1172 }1173 1174 /*1175 Removes gain map, if any.1176 */1177 VImage RemoveGainMap(VImage image) {1178 VImage copy = image.copy();1179 copy.remove("gainmap");1180 copy.remove("gainmap-data");1181 return copy;1182 }1183} // namespace sharp1184 