mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 02:30:12 +02:00
Initial commit
This commit is contained in:
parent
3db3405466
commit
a4149ed2a5
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,5 +10,6 @@ lib-cov
|
||||
pids
|
||||
logs
|
||||
results
|
||||
build
|
||||
|
||||
npm-debug.log
|
||||
|
9
.travis.yml
Normal file
9
.travis.yml
Normal file
@ -0,0 +1,9 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libvips-dev
|
||||
- sudo ln -s /usr/lib/pkgconfig/vips-7.26.pc /usr/lib/pkgconfig/vips.pc
|
48
README.md
Normal file → Executable file
48
README.md
Normal file → Executable file
@ -1,4 +1,46 @@
|
||||
sharp
|
||||
=====
|
||||
# sharp (_adj_)
|
||||
|
||||
High performance Node.js module to resize JPEG images using the libvips image processing library
|
||||
1. clearly defined; distinct: a sharp photographic image.
|
||||
2. quick, brisk, or spirited.
|
||||
3. shrewd or astute: a sharp bargainer.
|
||||
4. (Informal.) very stylish: a sharp dresser; a sharp jacket.
|
||||
|
||||
The typical use case for this high performance Node.js module is to convert a large JPEG image to smaller JPEG images of varying dimensions.
|
||||
|
||||
It is somewhat opinionated in that it only deals with JPEG images, always obeys the requested dimensions by either cropping or embedding and insists on a mild sharpen of the resulting image.
|
||||
|
||||
Under the hood you'll find the blazingly fast [libvips](https://github.com/jcupitt/libvips) image processing library, originally created in 1989 at Birkbeck College and currently maintained by the University of Southampton.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Requires node-gyp and libvips-dev to build.
|
||||
|
||||
sudo npm install -g node-gyp
|
||||
sudo apt-get install libvips-dev
|
||||
|
||||
Requires vips-7.xx.pc (installed with libvips-dev) to be symlinked as /usr/lib/pkgconfig/vips.pc
|
||||
|
||||
Ubuntu 12.04 LTS:
|
||||
|
||||
sudo ln -s /usr/lib/pkgconfig/vips-7.26.pc /usr/lib/pkgconfig/vips.pc
|
||||
|
||||
Ubuntu 13.04:
|
||||
|
||||
sudo ln -s /usr/lib/x86_64-linux-gnu/pkgconfig/vips-7.28.pc /usr/lib/pkgconfig/vips.pc
|
||||
|
||||
## Install
|
||||
|
||||
npm install sharp
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
var sharp = require("sharp");
|
||||
var cropLandscape = sharp.resize("input.jpg", "output.jpg", 300, 200, "c");
|
||||
var embedPortraitWhiteBorder = sharp.resize("input.jpg", "output.jpg", 200, 300, "w");
|
||||
var embedPortraitBlackBorder = sharp.resize("input.jpg", "output.jpg", 200, 300, "b");
|
||||
```
|
||||
|
||||
## Testing [](https://travis-ci.org/lovell/sharp)
|
||||
|
||||
npm test
|
||||
|
14
binding.gyp
Executable file
14
binding.gyp
Executable file
@ -0,0 +1,14 @@
|
||||
{
|
||||
'targets': [{
|
||||
'target_name': 'sharp',
|
||||
'sources': ['src/sharp.cc'],
|
||||
'libraries': ['<!@(PKG_CONFIG_PATH="/usr/lib/pkgconfig" pkg-config --libs glib-2.0 vips)'],
|
||||
'include_dirs': [
|
||||
'/usr/include/glib-2.0',
|
||||
'/usr/lib/glib-2.0/include',
|
||||
'/usr/lib/x86_64-linux-gnu/glib-2.0/include'
|
||||
],
|
||||
'cflags': ['-fexceptions'],
|
||||
'cflags_cc': ['-fexceptions']
|
||||
}]
|
||||
}
|
25
package.json
Executable file
25
package.json
Executable file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"version": "0.0.1",
|
||||
"main": "./build/Release/sharp",
|
||||
"description": "High performance Node.js module to resize JPEG images using the libvips image processing library",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/lovell/sharp"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"keywords": [
|
||||
"jpeg",
|
||||
"resize",
|
||||
"thumbnail",
|
||||
"sharpen",
|
||||
"crop",
|
||||
"embed",
|
||||
"libvips",
|
||||
"fast"
|
||||
],
|
||||
"author": "Lovell Fuller",
|
||||
"license": "Apache 2.0"
|
||||
}
|
173
src/sharp.cc
Executable file
173
src/sharp.cc
Executable file
@ -0,0 +1,173 @@
|
||||
#include <node.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <vips/vips.h>
|
||||
|
||||
using namespace v8;
|
||||
|
||||
// Free VipsImage children when object goes out of scope
|
||||
// Thanks due to https://github.com/dosx/node-vips
|
||||
class ImageFreer {
|
||||
public:
|
||||
ImageFreer() {}
|
||||
~ImageFreer() {
|
||||
for (uint16_t i = 0; i < v_.size(); i++) {
|
||||
if (v_[i] != NULL) {
|
||||
g_object_unref(v_[i]);
|
||||
}
|
||||
}
|
||||
v_.clear();
|
||||
}
|
||||
void add(VipsImage* i) { v_.push_back(i); }
|
||||
private:
|
||||
std::vector<VipsImage*> v_;
|
||||
};
|
||||
|
||||
struct ResizeBaton {
|
||||
std::string src;
|
||||
std::string dst;
|
||||
int cols;
|
||||
int rows;
|
||||
bool crop;
|
||||
int embed;
|
||||
std::string err;
|
||||
Persistent<Function> callback;
|
||||
};
|
||||
|
||||
void ResizeAsync(uv_work_t *work) {
|
||||
ResizeBaton* baton = static_cast<ResizeBaton*>(work->data);
|
||||
|
||||
VipsImage *in = vips_image_new_mode((baton->src).c_str(), "p");
|
||||
im_jpeg2vips((baton->src).c_str(), in);
|
||||
if (in == NULL) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
ImageFreer freer;
|
||||
freer.add(in);
|
||||
|
||||
VipsImage* img = in;
|
||||
VipsImage* t[4];
|
||||
|
||||
if (im_open_local_array(img, t, 4, "temp", "p")) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
double xfactor = static_cast<double>(img->Xsize) / std::max(baton->cols, 1);
|
||||
double yfactor = static_cast<double>(img->Ysize) / std::max(baton->rows, 1);
|
||||
double factor = baton->crop ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
|
||||
factor = std::max(factor, 1.0);
|
||||
int shrink = floor(factor);
|
||||
double residual = shrink / factor;
|
||||
|
||||
// Use im_shrink with the integral reduction
|
||||
if (im_shrink(img, t[0], shrink, shrink)) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Use im_affinei with the remaining float part using bilinear interpolation
|
||||
if (im_affinei_all(t[0], t[1], vips_interpolate_bilinear_static(), residual, 0, 0, residual, 0, 0)) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
img = t[1];
|
||||
|
||||
if (baton->crop) {
|
||||
int width = std::min(img->Xsize, baton->cols);
|
||||
int height = std::min(img->Ysize, baton->rows);
|
||||
int left = (img->Xsize - width + 1) / 2;
|
||||
int top = (img->Ysize - height + 1) / 2;
|
||||
if (im_extract_area(img, t[2], left, top, width, height)) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
img = t[2];
|
||||
} else {
|
||||
int left = (baton->cols - img->Xsize) / 2;
|
||||
int top = (baton->rows - img->Ysize) / 2;
|
||||
if (im_embed(img, t[2], baton->embed, left, top, baton->cols, baton->rows)) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
img = t[2];
|
||||
}
|
||||
|
||||
// Mild sharpen
|
||||
INTMASK* sharpen = im_create_imaskv("sharpen", 3, 3,
|
||||
-1, -1, -1,
|
||||
-1, 32, -1,
|
||||
-1, -1, -1);
|
||||
sharpen->scale = 24;
|
||||
if (im_conv(img, t[3], sharpen)) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
return;
|
||||
}
|
||||
img = t[3];
|
||||
|
||||
if (im_vips2jpeg(img, baton->dst.c_str())) {
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeAsyncAfter(uv_work_t *work, int status) {
|
||||
HandleScope scope;
|
||||
|
||||
ResizeBaton *baton = static_cast<ResizeBaton*>(work->data);
|
||||
|
||||
Local<Value> argv[1];
|
||||
if (!baton->err.empty()) {
|
||||
argv[0] = String::New(baton->err.data(), baton->err.size());
|
||||
} else {
|
||||
argv[0] = Local<Value>::New(Null());
|
||||
}
|
||||
|
||||
baton->callback->Call(Context::GetCurrent()->Global(), 1, argv);
|
||||
baton->callback.Dispose();
|
||||
delete baton;
|
||||
delete work;
|
||||
}
|
||||
|
||||
Handle<Value> Resize(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
ResizeBaton *baton = new ResizeBaton;
|
||||
baton->src = *String::Utf8Value(args[0]->ToString());
|
||||
baton->dst = *String::Utf8Value(args[1]->ToString());
|
||||
baton->cols = args[2]->Int32Value();
|
||||
baton->rows = args[3]->Int32Value();
|
||||
Local<String> canvas = args[4]->ToString();
|
||||
if (canvas->Equals(String::NewSymbol("c"))) {
|
||||
baton->crop = true;
|
||||
} else if (canvas->Equals(String::NewSymbol("w"))) {
|
||||
baton->crop = false;
|
||||
baton->embed = 4;
|
||||
} else if (canvas->Equals(String::NewSymbol("b"))) {
|
||||
baton->crop = false;
|
||||
baton->embed = 0;
|
||||
}
|
||||
baton->callback = Persistent<Function>::New(Local<Function>::Cast(args[5]));
|
||||
|
||||
uv_work_t *work = new uv_work_t;
|
||||
work->data = baton;
|
||||
uv_queue_work(uv_default_loop(), work, ResizeAsync, (uv_after_work_cb)ResizeAsyncAfter);
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
extern "C" void init(Handle<Object> target) {
|
||||
HandleScope scope;
|
||||
vips_init("");
|
||||
NODE_SET_METHOD(target, "resize", Resize);
|
||||
};
|
||||
|
||||
NODE_MODULE(vips, init)
|
Loading…
x
Reference in New Issue
Block a user