Compare commits

...

15 Commits

Author SHA1 Message Date
Lovell Fuller
a9aa7339ce Prerelease 0.33.0-alpha.3 2023-10-06 09:48:44 +01:00
Lovell Fuller
08108f5fad Use std::once for vips_init call 2023-10-06 09:44:57 +01:00
Lovell Fuller
58e3c4c70e Ensure all package files make use of commonjs explicit 2023-10-05 20:00:32 +01:00
Lovell Fuller
f8cf25ca56 Ensure correct interp of 16-bit raw input #3808 2023-10-05 14:05:36 +01:00
Lovell Fuller
ca95979ecc Prefix node builtins, skips cache lookup 2023-10-05 12:09:19 +01:00
Lovell Fuller
392f6afb5e Commit fcc7e84 but do it properly this time 2023-10-04 21:30:05 +01:00
Lovell Fuller
7c97aabaf8 Ensure win32 packages contain version/notice files 2023-10-04 21:12:16 +01:00
Lovell Fuller
fcc7e84bee Revert target names as these are used for DLLs 2023-10-04 20:46:25 +01:00
Lovell Fuller
226a9a13ef Packaging: prerelease version bump 2023-10-04 15:48:23 +01:00
Lovell Fuller
4d3c9ae3d1 Packaging: clear existing lib directories 2023-10-04 15:47:57 +01:00
Lovell Fuller
f31011d759 CI: increase linux-arm timeout 2023-10-04 15:47:35 +01:00
Lovell Fuller
7cf4ae5648 Prerelease 0.33.0-alpha.2 2023-10-04 10:16:35 +01:00
Lovell Fuller
9161c605e1 Clarify extract-resize-extract operation ordering 2023-10-03 19:28:18 +01:00
Lovell Fuller
70ac6905c7 Use std::atomic for counters 2023-09-30 14:01:04 +01:00
Lovell Fuller
265d70111a Add licensing info to npm readme files 2023-09-28 15:42:13 +01:00
29 changed files with 157 additions and 71 deletions

View File

@@ -141,7 +141,7 @@ jobs:
npm pkg set "optionalDependencies.@sharpen/sharp-linux-arm=file:./npm/linux-arm"
npm run clean
npm install --ignore-scripts
npx mocha --no-config --spec=test/unit/io.js
npx mocha --no-config --spec=test/unit/io.js --timeout=30000
[[ -n $prebuild_upload ]] && npx prebuild || true
macstadium-runner:
permissions:

View File

@@ -10,7 +10,7 @@
'sharp_libvips_lib_dir': '<!(node -p "require(\'./lib/libvips\').buildSharpLibvipsLibDir()")'
},
'targets': [{
'target_name': 'win-libvips-cpp',
'target_name': 'libvips-cpp',
'conditions': [
['OS == "win"', {
# Build libvips C++ binding for Windows due to MSVC std library ABI changes
@@ -81,7 +81,7 @@
],
'dependencies': [
'<!(node -p "require(\'node-addon-api\').gyp")',
'win-libvips-cpp'
'libvips-cpp'
],
'variables': {
'conditions': [
@@ -243,7 +243,7 @@
}
},
}, {
'target_name': 'win-copy-dlls',
'target_name': 'copy-dll',
'type': 'none',
'dependencies': [
'sharp-<(platform_and_arch)'

View File

@@ -214,7 +214,7 @@ Extract/crop a region of the image.
- Use `extract` before `resize` for pre-resize extraction.
- Use `extract` after `resize` for post-resize extraction.
- Use `extract` before and after for both.
- Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
**Throws**:

View File

@@ -13,6 +13,9 @@ Requires libvips v8.14.5
* Make `compression` option of `heif` mandatory to help reduce HEIF vs HEIC confusion.
[#3740](https://github.com/lovell/sharp/issues/3740)
* Ensure correct interpretation of 16-bit raw input.
[#3808](https://github.com/lovell/sharp/issues/3808)
## v0.32 - *flow*
Requires libvips v8.14.5

View File

@@ -3,8 +3,8 @@
'use strict';
const util = require('util');
const stream = require('stream');
const util = require('node:util');
const stream = require('node:stream');
const is = require('./is');
require('./sharp');

View File

@@ -3,7 +3,7 @@
'use strict';
const spawnSync = require('child_process').spawnSync;
const { spawnSync } = require('node:child_process');
const semverCoerce = require('semver/functions/coerce');
const semverGreaterThanOrEqualTo = require('semver/functions/gte');
const detectLibc = require('detect-libc');

View File

@@ -3,7 +3,7 @@
'use strict';
const path = require('path');
const path = require('node:path');
const is = require('./is');
const sharp = require('./sharp');

View File

@@ -250,6 +250,9 @@ function resize (widthOrOptions, height, options) {
if (isResizeExpected(this.options)) {
this.options.debuglog('ignoring previous resize options');
}
if (this.options.widthPost !== -1) {
this.options.debuglog('operation order will be: extract, resize, extract');
}
if (is.defined(widthOrOptions)) {
if (is.object(widthOrOptions) && !is.defined(options)) {
options = widthOrOptions;
@@ -437,7 +440,7 @@ function extend (extend) {
*
* - Use `extract` before `resize` for pre-resize extraction.
* - Use `extract` after `resize` for post-resize extraction.
* - Use `extract` before and after for both.
* - Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
*
* @example
* sharp(input)

View File

@@ -3,7 +3,7 @@
'use strict';
const events = require('events');
const events = require('node:events');
const detectLibc = require('detect-libc');
const is = require('./is');

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-darwin-arm64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with macOS ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -22,6 +22,7 @@
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-darwin-arm64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-darwin-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with macOS x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -19,6 +19,10 @@
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-darwin-x64.node",
"./package": "./package.json"

View File

@@ -5,7 +5,7 @@
// Populate contents of all packages with the current GitHub release
const { writeFile, copyFile } = require('node:fs/promises');
const { writeFile, copyFile, rm } = require('node:fs/promises');
const path = require('node:path');
const { Readable } = require('node:stream');
const { pipeline } = require('node:stream/promises');
@@ -20,8 +20,25 @@ const mapTarballEntry = (header) => {
return header;
};
const licensing = `
## Licensing
Copyright 2013 Lovell Fuller and others.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
`;
workspaces.map(async platform => {
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v7-${platform}.tar.gz`;
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v9-${platform}.tar.gz`;
const dir = path.join(__dirname, platform);
const response = await fetch(url);
if (!response.ok) {
@@ -29,14 +46,16 @@ workspaces.map(async platform => {
return;
}
// Extract prebuild tarball
const lib = path.join(dir, 'lib');
await rm(lib, { recursive: true });
await pipeline(
Readable.fromWeb(response.body),
createGunzip(),
extract(path.join(dir, 'lib'), { map: mapTarballEntry })
extract(lib, { map: mapTarballEntry })
);
// Generate README
const { name, description } = require(`./${platform}/package.json`);
await writeFile(path.join(dir, 'README.md'), `# ${name}\n${description}`);
await writeFile(path.join(dir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
// Copy Apache-2.0 LICENSE
await copyFile(path.join(__dirname, '..', 'LICENSE'), path.join(dir, 'LICENSE'));
// Copy Windows-specific files

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linux-arm",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -22,6 +22,7 @@
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-arm.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linux-arm64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with Linux (glibc) ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -22,6 +22,7 @@
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-arm64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linux-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with Linux (glibc) x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -22,6 +22,7 @@
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-x64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linuxmusl-arm64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with Linux (musl) ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -22,6 +22,7 @@
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linuxmusl-arm64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linuxmusl-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with Linux (musl) x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -22,6 +22,7 @@
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linuxmusl-x64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-win32-ia32",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with Windows x86 (32-bit)",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,18 +13,19 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-win32-ia32": "0.0.1-alpha.1"
},
"files": [
"lib"
"lib",
"versions.json",
"THIRD-PARTY-NOTICES.md"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-win32-ia32.node",
"./package": "./package.json"
"./package": "./package.json",
"./versions": "./versions.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-win32-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.3",
"description": "Prebuilt sharp for use with Windows x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,15 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-win32-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
"lib",
"versions.json",
"THIRD-PARTY-NOTICES.md"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-win32-x64.node",
"./package": "./package.json"

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.33.0-alpha.1",
"version": "0.33.0-alpha.3",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -103,13 +103,14 @@
"docs-serve": "cd docs && npx serve",
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
},
"type": "commonjs",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"binding.gyp",
"install/**",
"lib/**",
"src/**"
"install",
"lib",
"src"
],
"repository": {
"type": "git",
@@ -140,15 +141,15 @@
"semver": "^7.5.4"
},
"optionalDependencies": {
"@sharpen/sharp-darwin-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-darwin-x64": "0.0.1-alpha.1",
"@sharpen/sharp-linux-arm": "0.0.1-alpha.1",
"@sharpen/sharp-linux-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-linux-x64": "0.0.1-alpha.1",
"@sharpen/sharp-linuxmusl-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-linuxmusl-x64": "0.0.1-alpha.1",
"@sharpen/sharp-win32-ia32": "0.0.1-alpha.1",
"@sharpen/sharp-win32-x64": "0.0.1-alpha.1"
"@sharpen/sharp-darwin-arm64": "0.0.1-alpha.3",
"@sharpen/sharp-darwin-x64": "0.0.1-alpha.3",
"@sharpen/sharp-linux-arm": "0.0.1-alpha.3",
"@sharpen/sharp-linux-arm64": "0.0.1-alpha.3",
"@sharpen/sharp-linux-x64": "0.0.1-alpha.3",
"@sharpen/sharp-linuxmusl-arm64": "0.0.1-alpha.3",
"@sharpen/sharp-linuxmusl-x64": "0.0.1-alpha.3",
"@sharpen/sharp-win32-ia32": "0.0.1-alpha.3",
"@sharpen/sharp-win32-x64": "0.0.1-alpha.3"
},
"devDependencies": {
"@sharpen/sharp-libvips-darwin-arm64": "0.0.1-alpha.1",

View File

@@ -166,10 +166,10 @@ namespace sharp {
}
// How many tasks are in the queue?
volatile int counterQueue = 0;
std::atomic<int> counterQueue{0};
// How many tasks are being processed?
volatile int counterProcess = 0;
std::atomic<int> counterProcess{0};
// Filename extension checkers
static bool EndsWith(std::string const &str, std::string const &end) {
@@ -363,12 +363,13 @@ namespace sharp {
if (descriptor->isBuffer) {
if (descriptor->rawChannels > 0) {
// Raw, uncompressed pixel data
bool const is8bit = vips_band_format_is8bit(descriptor->rawDepth);
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, descriptor->rawDepth);
if (descriptor->rawChannels < 3) {
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_GREY16;
} else {
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
}
if (descriptor->rawPremultiplied) {
image = image.unpremultiply();

View File

@@ -7,6 +7,7 @@
#include <string>
#include <tuple>
#include <vector>
#include <atomic>
#include <napi.h>
#include <vips/vips8>
@@ -161,10 +162,10 @@ namespace sharp {
};
// How many tasks are in the queue?
extern volatile int counterQueue;
extern std::atomic<int> counterQueue;
// How many tasks are being processed?
extern volatile int counterProcess;
extern std::atomic<int> counterProcess;
// Filename extension checkers
bool IsJpeg(std::string const &str);

View File

@@ -18,7 +18,7 @@ class MetadataWorker : public Napi::AsyncWorker {
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
sharp::counterQueue--;
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
@@ -281,7 +281,7 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
sharp::counterQueue++;
return info.Env().Undefined();
}

View File

@@ -44,9 +44,9 @@ class PipelineWorker : public Napi::AsyncWorker {
// libuv worker
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
sharp::counterQueue--;
// Increment processing task counter
g_atomic_int_inc(&sharp::counterProcess);
sharp::counterProcess++;
try {
// Open input
@@ -1289,8 +1289,8 @@ class PipelineWorker : public Napi::AsyncWorker {
delete baton;
// Decrement processing task counter
g_atomic_int_dec_and_test(&sharp::counterProcess);
Napi::Number queueLength = Napi::Number::New(env, static_cast<double>(sharp::counterQueue));
sharp::counterProcess--;
Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue));
queueListener.MakeCallback(Receiver().Value(), { queueLength });
}
@@ -1707,8 +1707,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<double>(sharp::counterQueue));
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue));
queueListener.MakeCallback(info.This(), { queueLength });
return info.Env().Undefined();

View File

@@ -1,6 +1,8 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <mutex> // NOLINT(build/c++11)
#include <napi.h>
#include <vips/vips8>
@@ -10,14 +12,11 @@
#include "utilities.h"
#include "stats.h"
static void* sharp_vips_init(void*) {
vips_init("sharp");
return nullptr;
}
Napi::Object init(Napi::Env env, Napi::Object exports) {
static GOnce sharp_vips_init_once = G_ONCE_INIT;
g_once(&sharp_vips_init_once, static_cast<GThreadFunc>(sharp_vips_init), nullptr);
static std::once_flag sharp_vips_init_once;
std::call_once(sharp_vips_init_once, []() {
vips_init("sharp");
});
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);

View File

@@ -30,7 +30,7 @@ class StatsWorker : public Napi::AsyncWorker {
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
sharp::counterQueue--;
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
@@ -177,7 +177,7 @@ Napi::Value stats(const Napi::CallbackInfo& info) {
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
sharp::counterQueue++;
return info.Env().Undefined();
}

View File

@@ -70,8 +70,8 @@ Napi::Value concurrency(const Napi::CallbackInfo& info) {
*/
Napi::Value counters(const Napi::CallbackInfo& info) {
Napi::Object counters = Napi::Object::New(info.Env());
counters.Set("queue", sharp::counterQueue);
counters.Set("process", sharp::counterProcess);
counters.Set("queue", static_cast<int>(sharp::counterQueue));
counters.Set("process", static_cast<int>(sharp::counterProcess));
return counters;
}

View File

@@ -319,5 +319,16 @@ describe('Partial image extraction', function () {
s.extract(options);
assert.strictEqual(warningMessage, 'ignoring previous extract options');
});
it('Multiple extract+resize emits warning', () => {
let warningMessage = '';
const s = sharp();
s.on('warning', function (msg) { warningMessage = msg; });
const options = { top: 0, left: 0, width: 1, height: 1 };
s.extract(options).extract(options);
assert.strictEqual(warningMessage, '');
s.resize(1);
assert.strictEqual(warningMessage, 'operation order will be: extract, resize, extract');
});
});
});

View File

@@ -284,4 +284,42 @@ describe('Raw pixel data', function () {
);
}
});
describe('16-bit roundtrip', () => {
it('grey', async () => {
const grey = 42000;
const png = await sharp(
Uint16Array.from([grey]),
{ raw: { width: 1, height: 1, channels: 1 } }
)
.toColourspace('grey16')
.png({ compressionLevel: 0 })
.toBuffer();
const raw = await sharp(png)
.toColourspace('grey16')
.raw({ depth: 'ushort' })
.toBuffer();
assert.strictEqual(raw.readUint16LE(0), grey);
});
it('RGB', async () => {
const rgb = [10946, 28657, 46368];
const png = await sharp(
Uint16Array.from(rgb),
{ raw: { width: 1, height: 1, channels: 3 } }
)
.toColourspace('rgb16')
.png({ compressionLevel: 0 })
.toBuffer();
const raw = await sharp(png)
.toColourspace('rgb16')
.raw({ depth: 'ushort' })
.toBuffer();
assert.strictEqual(raw.readUint16LE(0), rgb[0]);
assert.strictEqual(raw.readUint16LE(2), rgb[1]);
assert.strictEqual(raw.readUint16LE(4), rgb[2]);
});
});
});