diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ba7db2c3..f7731b608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to wasm-vips will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v0.0.11] - TBD + +Uses libvips v8.16.0, compiled with Emscripten v3.1.64. + +### Changed + +- Update methods/enums for libvips 8.16. + ## [v0.0.10] - 2024-08-14 Uses libvips v8.15.3, compiled with Emscripten v3.1.64. @@ -185,6 +193,7 @@ Uses libvips v8.10.0, compiled with Emscripten v2.0.0. - Initial release. +[v0.0.11]: https://github.com/kleisauke/wasm-vips/compare/v0.0.10...v0.0.11 [v0.0.10]: https://github.com/kleisauke/wasm-vips/compare/v0.0.9...v0.0.10 [v0.0.9]: https://github.com/kleisauke/wasm-vips/compare/v0.0.8...v0.0.9 [v0.0.8]: https://github.com/kleisauke/wasm-vips/compare/v0.0.7...v0.0.8 diff --git a/build.sh b/build.sh index bbc8e7e81..00f5bd9b8 100755 --- a/build.sh +++ b/build.sh @@ -180,7 +180,7 @@ VERSION_TIFF=4.6.0 # https://gitlab.com/libtiff/libtiff VERSION_RESVG=0.43.0 # https://github.com/RazrFalcon/resvg VERSION_AOM=3.9.1 # https://aomedia.googlesource.com/aom VERSION_HEIF=1.18.2 # https://github.com/strukturag/libheif -VERSION_VIPS=8.15.3 # https://github.com/libvips/libvips +VERSION_VIPS=fad198f # https://github.com/libvips/libvips VERSION_EMSCRIPTEN="$(emcc -dumpversion)" @@ -486,12 +486,12 @@ EOL [ -f "$TARGET/lib/pkgconfig/vips.pc" ] || ( stage "Compiling vips" mkdir $DEPS/vips - curl -Ls https://github.com/libvips/libvips/releases/download/v$VERSION_VIPS/vips-$(without_prerelease $VERSION_VIPS).tar.xz | tar xJC $DEPS/vips --strip-components=1 + curl -Ls https://github.com/libvips/libvips/archive/$VERSION_VIPS.tar.gz | tar xzC $DEPS/vips --strip-components=1 cd $DEPS/vips # Emscripten specific patches - curl -Ls https://github.com/libvips/libvips/compare/v$VERSION_VIPS...kleisauke:wasm-vips-8.15.patch | patch -p1 + curl -Ls https://github.com/libvips/libvips/compare/$VERSION_VIPS...kleisauke:wasm-vips-8.16.patch | patch -p1 # Disable HBR support in heifsave - curl -Ls https://github.com/libvips/build-win64-mxe/raw/v$VERSION_VIPS/build/patches/vips-8-heifsave-disable-hbr-support.patch | patch -p1 + curl -Ls https://github.com/kleisauke/libvips/commit/88b4db95ec6c13860638a0a94e7ef44ea7ce0c19.patch | patch -p1 # Disable building man pages, gettext po files, tools, and (fuzz-)tests sed -i "/subdir('man')/{N;N;N;N;d;}" meson.build meson setup _build --prefix=$TARGET --cross-file=$MESON_CROSS --default-library=static --buildtype=release \ diff --git a/lib/vips.d.ts b/lib/vips.d.ts index 3eff6a44d..94c21d44e 100644 --- a/lib/vips.d.ts +++ b/lib/vips.d.ts @@ -2346,6 +2346,30 @@ declare module Vips { none = 3 // 'none' } + /** + * The SDF to generate, + * + * See also: vips_sdf(). + */ + enum SdfShape { + /** + * A circle at @a, radius @r + */ + circle = 0, // 'circle' + /** + * A box from @a to @b + */ + box = 1, // 'box' + /** + * A box with rounded @corners from @a to @b + */ + rounded_box = 2, // 'rounded-box' + /** + * A line from @a to @b + */ + line = 3 // 'line' + } + /** * How sensitive loaders are to errors, from never stop (very insensitive), to * stop on the smallest warning (very sensitive). @@ -3710,6 +3734,14 @@ declare module Vips { * @return Output image. */ static jxlload(filename: string, options?: { + /** + * First page to load. + */ + page?: number + /** + * Number of pages to load, -1 for all. + */ + n?: number /** * Force open via memory. */ @@ -3739,6 +3771,14 @@ declare module Vips { * @return Output image. */ static jxlloadBuffer(buffer: Blob, options?: { + /** + * First page to load. + */ + page?: number + /** + * Number of pages to load, -1 for all. + */ + n?: number /** * Force open via memory. */ @@ -3768,6 +3808,14 @@ declare module Vips { * @return Output image. */ static jxlloadSource(source: Source, options?: { + /** + * First page to load. + */ + page?: number + /** + * Number of pages to load, -1 for all. + */ + n?: number /** * Force open via memory. */ @@ -4926,6 +4974,33 @@ declare module Vips { flags?: number | undefined }): Image; + /** + * Create an sdf image. + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param shape SDF shape to create. + * @param options Optional options. + * @return Output image. + */ + static sdf(width: number, height: number, shape: SdfShape | Enum, options?: { + /** + * Radius. + */ + r?: number + /** + * Point a. + */ + a?: ArrayConstant + /** + * Point b. + */ + b?: ArrayConstant + /** + * Corner radii. + */ + corners?: ArrayConstant + }): Image; + /** * Make a 2d sine wave. * @param width Image width in pixels. @@ -5869,6 +5944,12 @@ declare module Vips { */ add(right: Image | ArrayConstant): Image; + /** + * Append an alpha channel. + * @return Output image. + */ + addalpha(): Image; + /** * Affine transform of an image. * @param matrix Transformation matrix. @@ -5993,26 +6074,6 @@ declare module Vips { */ byteswap(): Image; - /** - * Cache an image. - * @param options Optional options. - * @return Output image. - */ - cache(options?: { - /** - * Maximum number of tiles to cache. - */ - max_tiles?: number - /** - * Tile height in pixels. - */ - tile_height?: number - /** - * Tile width in pixels. - */ - tile_width?: number - }): Image; - /** * Canny edge detector. * @param options Optional options. @@ -6049,6 +6110,22 @@ declare module Vips { shift?: boolean }): Image; + /** + * Clamp values of an image. + * @param options Optional options. + * @return Output image. + */ + clamp(options?: { + /** + * Minimum value. + */ + min?: number + /** + * Maximum value. + */ + max?: number + }): Image; + /** * Convert to a new colorspace. * @param space Destination color space. @@ -7581,7 +7658,7 @@ declare module Vips { /** * Save image in jpeg2000 format. - * @param filename Filename to load from. + * @param filename Filename to save to. * @param options Optional options. */ jp2ksave(filename: string, options?: { @@ -7952,7 +8029,7 @@ declare module Vips { /** * Save image in jpeg-xl format. - * @param filename Filename to load from. + * @param filename Filename to save to. * @param options Optional options. */ jxlsave(filename: string, options?: { @@ -8415,6 +8492,13 @@ declare module Vips { y_array?: number[] | undefined }): number; + /** + * Maximum of a pair of images. + * @param right Right-hand image argument. + * @return Output image. + */ + maxpair(right: Image | ArrayConstant): Image; + /** * Measure a set of patches on a color chart. * @param h Number of patches across chart. @@ -8489,6 +8573,13 @@ declare module Vips { y_array?: number[] | undefined }): number; + /** + * Minimum of a pair of images. + * @param right Right-hand image argument. + * @return Output image. + */ + minpair(right: Image | ArrayConstant): Image; + /** * Morphology operation. * @param mask Input matrix image. @@ -9029,11 +9120,35 @@ declare module Vips { }): void; /** - * Write raw image to file descriptor. - * @param fd File descriptor to write to. + * Write raw image to buffer. * @param options Optional options. + * @return Buffer to save to. */ - rawsaveFd(fd: number, options?: { + rawsaveBuffer(options?: { + /** + * Which metadata to retain. + */ + keep?: ForeignKeep | Flag + /** + * Background value. + */ + background?: ArrayConstant + /** + * Set page height for multipage save. + */ + page_height?: number + /** + * Filename of icc profile to embed. + */ + profile?: string + }): Uint8Array; + + /** + * Write raw image to target. + * @param target Target to save to. + * @param options Optional options. + */ + rawsaveTarget(target: Target, options?: { /** * Which metadata to retain. */ @@ -9176,7 +9291,7 @@ declare module Vips { /** * Rotate an image by a number of degrees. - * @param angle Rotate anticlockwise by this many degrees. + * @param angle Rotate clockwise by this many degrees. * @param options Optional options. * @return Output image. */ @@ -9379,7 +9494,7 @@ declare module Vips { */ scale?: number /** - * Rotate anticlockwise by this many degrees. + * Rotate clockwise by this many degrees. */ angle?: number /** @@ -10017,10 +10132,18 @@ declare module Vips { * Level of cpu effort to reduce file size. */ effort?: number + /** + * Desired target size in bytes. + */ + target_size?: number /** * Allow mixed encoding (might reduce file size). */ mixed?: boolean + /** + * Number of entropy-analysis passes (in [1..10]). + */ + passes?: number /** * Which metadata to retain. */ @@ -10085,10 +10208,18 @@ declare module Vips { * Level of cpu effort to reduce file size. */ effort?: number + /** + * Desired target size in bytes. + */ + target_size?: number /** * Allow mixed encoding (might reduce file size). */ mixed?: boolean + /** + * Number of entropy-analysis passes (in [1..10]). + */ + passes?: number /** * Which metadata to retain. */ @@ -10152,10 +10283,18 @@ declare module Vips { * Level of cpu effort to reduce file size. */ effort?: number + /** + * Desired target size in bytes. + */ + target_size?: number /** * Allow mixed encoding (might reduce file size). */ mixed?: boolean + /** + * Number of entropy-analysis passes (in [1..10]). + */ + passes?: number /** * Which metadata to retain. */ @@ -10220,10 +10359,18 @@ declare module Vips { * Level of cpu effort to reduce file size. */ effort?: number + /** + * Desired target size in bytes. + */ + target_size?: number /** * Allow mixed encoding (might reduce file size). */ mixed?: boolean + /** + * Number of entropy-analysis passes (in [1..10]). + */ + passes?: number /** * Which metadata to retain. */ diff --git a/src/bindings/vips-operators.cpp b/src/bindings/vips-operators.cpp index 677fd369d..5f10cde18 100644 --- a/src/bindings/vips-operators.cpp +++ b/src/bindings/vips-operators.cpp @@ -917,6 +917,21 @@ Image Image::rawload(const std::string &filename, int width, int height, int ban return out; } +Image Image::sdf(int width, int height, emscripten::val shape, emscripten::val js_options) +{ + Image out; + + Image::call("sdf", nullptr, + (new Option) + ->set("out", &out) + ->set("width", width) + ->set("height", height) + ->set("shape", VIPS_TYPE_SDF_SHAPE, shape), + js_options); + + return out; +} + Image Image::sines(int width, int height, emscripten::val js_options) { Image out; @@ -1465,6 +1480,18 @@ Image Image::abs() const return out; } +Image Image::addalpha() const +{ + Image out; + + this->call("addalpha", + (new Option) + ->set("in", *this) + ->set("out", &out)); + + return out; +} + Image Image::affine(const std::vector &matrix, emscripten::val js_options) const { Image out; @@ -1593,19 +1620,6 @@ Image Image::byteswap() const return out; } -Image Image::cache(emscripten::val js_options) const -{ - Image out; - - this->call("cache", - (new Option) - ->set("in", *this) - ->set("out", &out), - js_options); - - return out; -} - Image Image::canny(emscripten::val js_options) const { Image out; @@ -1646,6 +1660,19 @@ Image Image::cast(emscripten::val format, emscripten::val js_options) const return out; } +Image Image::clamp(emscripten::val js_options) const +{ + Image out; + + this->call("clamp", + (new Option) + ->set("in", *this) + ->set("out", &out), + js_options); + + return out; +} + Image Image::colourspace(emscripten::val space, emscripten::val js_options) const { Image out; @@ -2970,6 +2997,19 @@ double Image::max(emscripten::val js_options) const return out; } +Image Image::maxpair(emscripten::val right) const +{ + Image out; + + this->call("maxpair", + (new Option) + ->set("left", *this) + ->set("out", &out) + ->set("right", VIPS_TYPE_IMAGE, right, this)); + + return out; +} + Image Image::measure(int h, int v, emscripten::val js_options) const { Image out; @@ -3015,6 +3055,19 @@ double Image::min(emscripten::val js_options) const return out; } +Image Image::minpair(emscripten::val right) const +{ + Image out; + + this->call("minpair", + (new Option) + ->set("left", *this) + ->set("out", &out) + ->set("right", VIPS_TYPE_IMAGE, right, this)); + + return out; +} + Image Image::morph(emscripten::val mask, emscripten::val morph) const { Image out; @@ -3310,12 +3363,30 @@ void Image::rawsave(const std::string &filename, emscripten::val js_options) con js_options); } -void Image::rawsave_fd(int fd, emscripten::val js_options) const +emscripten::val Image::rawsave_buffer(emscripten::val js_options) const +{ + VipsBlob *buffer; + + this->call("rawsave_buffer", + (new Option) + ->set("in", *this) + ->set("buffer", &buffer), + js_options); + + emscripten::val result = BlobVal.new_(emscripten::typed_memory_view( + VIPS_AREA(buffer)->length, + reinterpret_cast(VIPS_AREA(buffer)->data))); + vips_area_unref(VIPS_AREA(buffer)); + + return result; +} + +void Image::rawsave_target(const Target &target, emscripten::val js_options) const { - this->call("rawsave_fd", + this->call("rawsave_target", (new Option) ->set("in", *this) - ->set("fd", fd), + ->set("target", target), js_options); } diff --git a/src/bindings/vips-operators.h b/src/bindings/vips-operators.h index a5837bb92..b468d47bc 100644 --- a/src/bindings/vips-operators.h +++ b/src/bindings/vips-operators.h @@ -569,6 +569,16 @@ static Image radload_source(const Source &source, emscripten::val js_options = e */ static Image rawload(const std::string &filename, int width, int height, int bands, emscripten::val js_options = emscripten::val::null()); +/** + * Create an sdf image. + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param shape SDF shape to create. + * @param js_options Optional options. + * @return Output image. + */ +static Image sdf(int width, int height, emscripten::val shape, emscripten::val js_options = emscripten::val::null()); + /** * Make a 2d sine wave. * @param width Image width in pixels. @@ -878,6 +888,12 @@ Image Yxy2XYZ() const; */ Image abs() const; +/** + * Append an alpha channel. + * @return Output image. + */ +Image addalpha() const; + /** * Affine transform of an image. * @param matrix Transformation matrix. @@ -946,13 +962,6 @@ Image buildlut() const; */ Image byteswap() const; -/** - * Cache an image. - * @param js_options Optional options. - * @return Output image. - */ -Image cache(emscripten::val js_options = emscripten::val::null()) const; - /** * Canny edge detector. * @param js_options Optional options. @@ -975,6 +984,13 @@ Image case_image(emscripten::val cases) const; */ Image cast(emscripten::val format, emscripten::val js_options = emscripten::val::null()) const; +/** + * Clamp values of an image. + * @param js_options Optional options. + * @return Output image. + */ +Image clamp(emscripten::val js_options = emscripten::val::null()) const; + /** * Convert to a new colorspace. * @param space Destination color space. @@ -1570,7 +1586,7 @@ Image join(emscripten::val in2, emscripten::val direction, emscripten::val js_op /** * Save image in jpeg2000 format. - * @param filename Filename to load from. + * @param filename Filename to save to. * @param js_options Optional options. */ void jp2ksave(const std::string &filename, emscripten::val js_options = emscripten::val::null()) const; @@ -1618,7 +1634,7 @@ void jpegsave_target(const Target &target, emscripten::val js_options = emscript /** * Save image in jpeg-xl format. - * @param filename Filename to load from. + * @param filename Filename to save to. * @param js_options Optional options. */ void jxlsave(const std::string &filename, emscripten::val js_options = emscripten::val::null()) const; @@ -1754,6 +1770,13 @@ void matrixsave_target(const Target &target, emscripten::val js_options = emscri */ double max(emscripten::val js_options = emscripten::val::null()) const; +/** + * Maximum of a pair of images. + * @param right Right-hand image argument. + * @return Output image. + */ +Image maxpair(emscripten::val right) const; + /** * Measure a set of patches on a color chart. * @param h Number of patches across chart. @@ -1781,6 +1804,13 @@ Image merge(emscripten::val sec, emscripten::val direction, int dx, int dy, emsc */ double min(emscripten::val js_options = emscripten::val::null()) const; +/** + * Minimum of a pair of images. + * @param right Right-hand image argument. + * @return Output image. + */ +Image minpair(emscripten::val right) const; + /** * Morphology operation. * @param mask Input matrix image. @@ -1961,11 +1991,18 @@ Image rank(int width, int height, int index) const; void rawsave(const std::string &filename, emscripten::val js_options = emscripten::val::null()) const; /** - * Write raw image to file descriptor. - * @param fd File descriptor to write to. + * Write raw image to buffer. + * @param js_options Optional options. + * @return Buffer to save to. + */ +emscripten::val rawsave_buffer(emscripten::val js_options = emscripten::val::null()) const; + +/** + * Write raw image to target. + * @param target Target to save to. * @param js_options Optional options. */ -void rawsave_fd(int fd, emscripten::val js_options = emscripten::val::null()) const; +void rawsave_target(const Target &target, emscripten::val js_options = emscripten::val::null()) const; /** * Linear recombination with matrix. @@ -2046,7 +2083,7 @@ Image rot45(emscripten::val js_options = emscripten::val::null()) const; /** * Rotate an image by a number of degrees. - * @param angle Rotate anticlockwise by this many degrees. + * @param angle Rotate clockwise by this many degrees. * @param js_options Optional options. * @return Output image. */ diff --git a/src/vips-emscripten.cpp b/src/vips-emscripten.cpp index 980a22c89..34b9b4096 100644 --- a/src/vips-emscripten.cpp +++ b/src/vips-emscripten.cpp @@ -283,6 +283,12 @@ EMSCRIPTEN_BINDINGS(my_module) { .value("word_char", VIPS_TEXT_WRAP_WORD_CHAR) .value("none", VIPS_TEXT_WRAP_NONE); + enum_("SdfShape") + .value("circle", VIPS_SDF_SHAPE_CIRCLE) + .value("box", VIPS_SDF_SHAPE_BOX) + .value("rounded_box", VIPS_SDF_SHAPE_ROUNDED_BOX) + .value("line", VIPS_SDF_SHAPE_LINE); + enum_("FailOn") .value("none", VIPS_FAIL_ON_NONE) .value("truncated", VIPS_FAIL_ON_TRUNCATED) @@ -1362,6 +1368,10 @@ EMSCRIPTEN_BINDINGS(my_module) { .class_function("rawload", optional_override([](const std::string &filename, int width, int height, int bands) { return Image::rawload(filename, width, height, bands); })) + .class_function("sdf", &Image::sdf) + .class_function("sdf", optional_override([](int width, int height, emscripten::val shape) { + return Image::sdf(width, height, shape); + })) .class_function("sines", &Image::sines) .class_function("sines", optional_override([](int width, int height) { return Image::sines(width, height); @@ -1474,6 +1484,7 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("XYZ2scRGB", &Image::XYZ2scRGB) .function("Yxy2XYZ", &Image::Yxy2XYZ) .function("abs", &Image::abs) + .function("addalpha", &Image::addalpha) .function("affine", &Image::affine) .function("affine", optional_override([](const Image &image, const std::vector &matrix) { return image.affine(matrix); @@ -1494,10 +1505,6 @@ EMSCRIPTEN_BINDINGS(my_module) { })) .function("buildlut", &Image::buildlut) .function("byteswap", &Image::byteswap) - .function("cache", &Image::cache) - .function("cache", optional_override([](const Image &image) { - return image.cache(); - })) .function("canny", &Image::canny) .function("canny", optional_override([](const Image &image) { return image.canny(); @@ -1507,6 +1514,10 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("cast", optional_override([](const Image &image, emscripten::val format) { return image.cast(format); })) + .function("clamp", &Image::clamp) + .function("clamp", optional_override([](const Image &image) { + return image.clamp(); + })) .function("colourspace", &Image::colourspace) .function("colourspace", optional_override([](const Image &image, emscripten::val space) { return image.colourspace(space); @@ -1808,6 +1819,7 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("max", optional_override([](const Image &image) { return image.max(); })) + .function("maxpair", &Image::maxpair) .function("measure", &Image::measure) .function("measure", optional_override([](const Image &image, int h, int v) { return image.measure(h, v); @@ -1820,6 +1832,7 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("min", optional_override([](const Image &image) { return image.min(); })) + .function("minpair", &Image::minpair) .function("mosaic", &Image::mosaic) .function("mosaic", optional_override([](const Image &image, emscripten::val sec, emscripten::val direction, int xref, int yref, int xsec, int ysec) { return image.mosaic(sec, direction, xref, yref, xsec, ysec); @@ -1885,9 +1898,13 @@ EMSCRIPTEN_BINDINGS(my_module) { .function("rawsave", optional_override([](const Image &image, const std::string &filename) { image.rawsave(filename); })) - .function("rawsaveFd", &Image::rawsave_fd) - .function("rawsaveFd", optional_override([](const Image &image, int fd) { - image.rawsave_fd(fd); + .function("rawsaveBuffer", &Image::rawsave_buffer) + .function("rawsaveBuffer", optional_override([](const Image &image) { + return image.rawsave_buffer(); + })) + .function("rawsaveTarget", &Image::rawsave_target) + .function("rawsaveTarget", optional_override([](const Image &image, const Target &target) { + image.rawsave_target(target); })) .function("recomb", &Image::recomb) .function("reduce", &Image::reduce) diff --git a/test/unit/test_arithmetic.js b/test/unit/test_arithmetic.js index fcabddeff..06e970d00 100644 --- a/test/unit/test_arithmetic.js +++ b/test/unit/test_arithmetic.js @@ -841,4 +841,41 @@ describe('arithmetic', () => { expect(im3.max()).to.be.closeTo(sum, 1e-6); } }); + + it('clamp', function () { + for (const fmt of Helpers.noncomplexFormats) { + for (let x = 0; x < 100; x += 10) { + const im2 = colour.add(x).cast(fmt); + let im3 = im2.clamp(); + expect(im3.max()).to.be.at.most(1.0); + expect(im3.min()).to.be.at.least(0.0); + + im3 = im2.clamp({ min: 14, max: 45 }); + expect(im3.max()).to.be.at.most(45); + expect(im3.min()).to.be.at.least(14); + } + } + }); + + it('minpair', function () { + for (const fmt of Helpers.noncomplexFormats) { + for (let x = 0; x < 100; x += 10) { + const im2 = colour.subtract(x).multiply(5).cast(fmt); + const im3 = im2.minpair(colour); + const im4 = im2.less(colour).ifthenelse(im2, colour); + expect(im3.subtract(im4).abs().max()).to.equal(0.0); + } + } + }); + + it('maxpair', function () { + for (const fmt of Helpers.noncomplexFormats) { + for (let x = 0; x < 100; x += 10) { + const im2 = colour.subtract(x).multiply(5).cast(fmt); + const im3 = im2.maxpair(colour); + const im4 = im2.more(colour).ifthenelse(im2, colour); + expect(im3.subtract(im4).abs().max()).to.equal(0.0); + } + } + }); }); diff --git a/test/unit/test_conversion.js b/test/unit/test_conversion.js index 9aed76741..eb2c547f9 100644 --- a/test/unit/test_conversion.js +++ b/test/unit/test_conversion.js @@ -117,6 +117,12 @@ describe('conversion', () => { expect(x.extractBand(4).avg()).to.equal(2); }); + it('addalpha', function () { + const x = colour.addalpha(); + expect(x.bands).to.equal(4); + expect(x.extractBand(3).avg()).to.equal(255); + }); + it('bandmean', function () { const bandmean = (x) => x instanceof vips.Image ? x.bandmean() @@ -140,12 +146,6 @@ describe('conversion', () => { expect(a.subtract(b).abs().min()).to.equal(0); }); - it('cache', function () { - const cache = (x) => x instanceof vips.Image ? x.cache() : x; - - runUnary(allImages, cache); - }); - it('copy', function () { let x = colour.copy({ interpretation: 'lab' }); expect(x.interpretation).to.equal('lab'); diff --git a/test/unit/test_create.js b/test/unit/test_create.js index f3f7678b1..67ada9296 100644 --- a/test/unit/test_create.js +++ b/test/unit/test_create.js @@ -548,6 +548,53 @@ describe('create', () => { Helpers.assertAlmostEqualObjects(p, [45, 35]); }); + it('sdf', function () { + let im = vips.Image.sdf(128, 128, vips.SdfShape.circle, { + a: [64, 64], + r: 32 + }); + expect(im.bands).to.equal(1); + expect(im.format).to.equal('float'); + expect(im.width).to.equal(128); + expect(im.height).to.equal(128); + let p = im.getpoint(45, 35); + Helpers.assertAlmostEqualObjects(p, [2.670], 0.01); + + im = vips.Image.sdf(128, 128, vips.SdfShape.box, { + a: [10, 10], + b: [50, 40] + }); + expect(im.bands).to.equal(1); + expect(im.format).to.equal('float'); + expect(im.width).to.equal(128); + expect(im.height).to.equal(128); + p = im.getpoint(45, 35); + Helpers.assertAlmostEqualObjects(p, [-5.0]); + + im = vips.Image.sdf(128, 128, vips.SdfShape.rounded_box, { + a: [10, 10], + b: [50, 40], + corners: [50, 0, 0, 0] + }); + expect(im.bands).to.equal(1); + expect(im.format).to.equal('float'); + expect(im.width).to.equal(128); + expect(im.height).to.equal(128); + p = im.getpoint(45, 35); + Helpers.assertAlmostEqualObjects(p, [13.640], 0.01); + + im = vips.Image.sdf(128, 128, vips.SdfShape.line, { + a: [10, 10], + b: [50, 40] + }); + expect(im.bands).to.equal(1); + expect(im.format).to.equal('float'); + expect(im.width).to.equal(128); + expect(im.height).to.equal(128); + p = im.getpoint(45, 35); + Helpers.assertAlmostEqualObjects(p, [1.0]); + }); + it('zone', function () { const im = vips.Image.zone(128, 128); expect(im.width).to.equal(128); diff --git a/test/unit/test_foreign.js b/test/unit/test_foreign.js index db1439b05..be85be35a 100644 --- a/test/unit/test_foreign.js +++ b/test/unit/test_foreign.js @@ -148,7 +148,7 @@ describe('foreign', () => { it('vips', function () { // ftruncate() is not yet available in the Node backend of WasmFS. - // https://github.com/emscripten-core/emscripten/blob/3.1.48/system/lib/wasmfs/backends/node_backend.cpp#L120-L122 + // https://github.com/emscripten-core/emscripten/blob/3.1.64/system/lib/wasmfs/backends/node_backend.cpp#L120-L122 if (typeof vips.FS.statBufToObject === 'function') { return this.skip(); } @@ -400,6 +400,7 @@ describe('foreign', () => { expect(im.height).to.equal(442); expect(im.bands).to.equal(3); expect(im.getInt('bits-per-sample')).to.equal(16); + expect(im.getTypeof('palette')).to.equal(0); }; fileLoader('pngload', Helpers.pngFile, pngValid); @@ -752,6 +753,12 @@ describe('foreign', () => { expect(im.width).to.equal(13); expect(im.height).to.equal(16731); buf = im.webpsaveBuffer(); // eslint-disable-line no-unused-vars + + // target_size should reasonably work, +/- 2% is fine + im = vips.Image.newFromFile(Helpers.webpFile); + buf = im.webpsaveBuffer({ target_size: 20000, keep: vips.ForeignKeep.none }); + expect(buf.byteLength).to.be.below(20400); + expect(buf.byteLength).to.be.above(19600); }); it('gifload', function () { @@ -766,7 +773,6 @@ describe('foreign', () => { expect(im.width).to.equal(159); expect(im.height).to.equal(203); expect(im.bands).to.equal(3); - expect(im.getInt('bits-per-sample')).to.equal(4); }; fileLoader('gifload', Helpers.gifFile, gifValid); @@ -777,6 +783,8 @@ describe('foreign', () => { expect(x1.getInt('n-pages')).to.equal(1); expect(x1.getArrayDouble('background')).to.deep.equal([81.0, 81.0, 81.0]); expect(x1.getInt('interlaced')).to.equal(1); + expect(x1.getInt('bits-per-sample')).to.equal(4); + expect(x1.getInt('palette')).to.equal(1); x1 = vips.Image.newFromFile(Helpers.gifAnimFile, { n: -1 }); // our test gif has delay 0 for the first frame set in error @@ -784,6 +792,7 @@ describe('foreign', () => { expect(x1.getInt('loop')).to.equal(32761); expect(x1.getArrayDouble('background')).to.deep.equal([255.0, 255.0, 255.0]); expect(x1.getTypeof('interlaced')).to.equal(0); + expect(x1.getInt('palette')).to.equal(1); // test deprecated fields too expect(x1.getInt('gif-loop')).to.equal(32760); expect(x1.getInt('gif-delay')).to.equal(0); @@ -883,22 +892,25 @@ describe('foreign', () => { return this.skip(); } - saveLoad('%s.ppm', mono); saveLoad('%s.ppm', colour); - saveLoadFile('%s.ppm', '[ascii]', mono, 0); + saveLoadFile('%s.pgm', '[ascii]', mono, 0); saveLoadFile('%s.ppm', '[ascii]', colour, 0); - saveLoadFile('%s.ppm', '[ascii,bitdepth=1]', onebit, 0); + saveLoadFile('%s.pbm', '[ascii]', onebit, 0); const rgb16 = colour.colourspace('rgb16'); const grey16 = mono.colourspace('rgb16'); - saveLoad('%s.ppm', grey16); saveLoad('%s.ppm', rgb16); saveLoadFile('%s.ppm', '[ascii]', grey16, 0); saveLoadFile('%s.ppm', '[ascii]', rgb16, 0); + + const source = vips.Source.newFromMemory('P1\n#\n#\n1 1\n0\n'); + const im = vips.Image.ppmloadSource(source); + expect(im.width).to.equal(1); + expect(im.height).to.equal(1); }); it('radload', function () { @@ -964,6 +976,18 @@ describe('foreign', () => { im = vips.Image.newFromBuffer(svg, ''); expect(im.width).to.equal(1); expect(im.height).to.equal(1); + + // scale up + svg = ''; + im = vips.Image.newFromBuffer(svg, '', { scale: 10000 }); + expect(im.width).to.equal(10000); + expect(im.height).to.equal(10000); + + // scale down + svg = ''; + im = vips.Image.newFromBuffer(svg, '', { scale: 0.0001 }); + expect(im.width).to.equal(10); + expect(im.height).to.equal(10); }); it('heifload', function () { @@ -999,10 +1023,8 @@ describe('foreign', () => { return this.skip(); } - // TODO(kleisauke): Remove `subsample_mode: 'off'` when libvips >= 8.16, see: - // https://github.com/libvips/libvips/commit/dbd298cc8c9789dfc0fc6917b2492cb570406a7a saveLoadBuffer('heifsave_buffer', 'heifload_buffer', - colour, 0, { compression: 'av1', lossless: true, subsample_mode: 'off' }); + colour, 0, { compression: 'av1', lossless: true }); saveLoad('%s.avif', colour); }); diff --git a/test/unit/test_iofuncs.js b/test/unit/test_iofuncs.js index 1f2cb5458..c2f3e854e 100644 --- a/test/unit/test_iofuncs.js +++ b/test/unit/test_iofuncs.js @@ -65,7 +65,7 @@ describe('iofuncs', () => { it('revalidate', function () { // ftruncate() is not yet available in the Node backend of WasmFS. - // https://github.com/emscripten-core/emscripten/blob/3.1.48/system/lib/wasmfs/backends/node_backend.cpp#L120-L122 + // https://github.com/emscripten-core/emscripten/blob/3.1.64/system/lib/wasmfs/backends/node_backend.cpp#L120-L122 if (typeof vips.FS.statBufToObject === 'function') { return this.skip(); } diff --git a/test/unit/test_resample.js b/test/unit/test_resample.js index 2cda8f5ba..2ab579a06 100644 --- a/test/unit/test_resample.js +++ b/test/unit/test_resample.js @@ -268,6 +268,13 @@ describe('resample', () => { im2 = vips.Image.newFromFile(Helpers.rgbaCorrectFile); expect(Math.abs(im1.flatten({ background: 255 }).avg() - im2.avg())).to.be.below(1); } + + // thumbnailing a 16-bit image should always make an 8-bit image + const rgb16Buffer = vips.Image.newFromFile(Helpers.jpegFile) + .colourspace(vips.Interpretation.rgb16) + .writeToBuffer('.png'); + const thumb = vips.Image.thumbnailBuffer(rgb16Buffer, 128); + expect(thumb.format).to.equal('uchar'); }); describe('similarity', () => {