如何在node.js中使用graphicsmagick裁剪圆形图片(其他使用任何其他方式在node.js上制作)

How to use graphicsmagick in node.js to crop circle picture (other use any other way to make this on node.js)

如何使用graphicsmagick麦田圈图片?

(使用png透明背景外圈)

我在 node.js 中使用 graphicsmagick,或者有任何其他方法在 node.js?

之前: http://i.stack.imgur.com/B34C5.png

之后: http://i.stack.imgur.com/PWjQW.png

我使用另一个模块来解决这个问题:

此代码用于方形图像 (this.height = this.width)

node-pngjs

var fs = require('fs'),
PNG = require('pngjs').PNG;

fs.createReadStream(__dirname + "/input.png")
    .pipe(new PNG({
        filterType: 4
    }))
    .on('parsed', function() {
    for (var y = 0; y < this.height; y++) {
        for (var x = 0; x < this.width; x++) {
            var idx = (this.width * y + x) << 2;
            var radius = this.height / 2;
            if(y >= Math.sqrt(Math.pow(radius, 2) - Math.pow(x - radius, 2)) + radius || y <= -(Math.sqrt(Math.pow(radius, 2) - Math.pow(x - radius, 2))) + radius) {
                this.data[idx + 3] = 0;
            }
        }
    }
        this.pack().pipe(fs.createWriteStream(__dirname + "/output.png"));
    });

我可以使用以下插件做到这一点: https://www.npmjs.com/package/circle-image

安装后:

var images = require('circle-image');
var imageSizes = [125, 100, 30];
//uniqueId param is used to identify a user 
//so user the primary key or something guaranteed to be unique 
images.execute('imagepath', uniqueId, imageSizes).then(function (paths) {
  //array of circularized image paths 
  console.log(paths[0]); //circle_user_{uniqueId}_150.png 
})

经过几个小时的摆弄,我找到了一个使用 node-gm 和 Imagemagick 的干净解决方案。

var gm = require('gm').subClass({ imageMagick: true });

var original = 'app-server/photo.jpg'; 
var output = 'app-server/photo.png';
var size = 80;

gm(original)
  .crop(233, 233,29,26)
  .resize(size, size)
  .write(output, function() {

     gm(size, size, 'none')
        .fill(output)
        .drawCircle(size/2,size/2, size/2, 0)
        .write(output, function(err) {
           console.log(err || 'done');
        });
  });

我花了几个小时才弄明白

  1. 我想避免使用第三方软件,例如 ImageMagick(许多 npm 包都建立在它之上)
  2. node-pngjs 似乎只支持 PNG 文件 + 需要太多自定义代码

这是我使用 sharp 的解决方案:

const sharp = require('sharp');

const width = 400,
    r = width / 2,
    circleShape = Buffer.from(`<svg><circle cx="${r}" cy="${r}" r="${r}" /></svg>`);

sharp('input.jpg')
    .resize(width, width)
    .composite([{
        input: circleShape,
        blend: 'dest-in'
    }])
    .webp()
    .toFile('output.webp', (err, info) => err ?
        console.error(err.message) :
        console.log(info)
    );

这是使用 gm 和缓冲区的版本:

var gm = require('gm').subClass({ imageMagick: true });
var max = 200;
// buffer - is Buffer with picture

gm(buffer)
    .autoOrient()
    .gravity('Center')
    .resize(max, max, '^')
    .extent(max, max)
    .noProfile()
    .setFormat('png')
    .out('(')
        .rawSize(max, max)
        .out('xc:Black')
        .fill('White')
        .drawCircle(max/2,max/2, max/2, 1)
        .out('-alpha', 'Copy')
    .out(')')
    .compose('CopyOpacity')
    .out('-composite')
    .trim()
    .toBuffer((err, buffer) => {
        //...
    });

以上 node.js 代码等同于此 ImageMagick 命令,但使用缓冲区:

convert input.png \
        -gravity Center \
        -resize 200x200^ \
        -extent 200x200 \
        \( -size 200x200 \
           xc:Black \
           -fill White \
           -draw 'circle 100 100 100 1' \
           -alpha Copy \
        \) -compose CopyOpacity -composite \
        -trim output.png

使用裁剪

const gm = require("gm");
const path = require("path");
const fs = require("fs");

const pathIn = path.resolve(__dirname, "images/in.jpeg");
function circularize(path, callback) {
  const maskPath = path.replace(/\.[a-zA-Z]+$/g, "_mask.png");
  const tmpPath = path.replace(/\.[a-zA-Z]+$/g, "_tmp.png");
  gm(pathIn).size((err, iSize) => {
    if (err) return callback(err, null);
    const { width, height } = iSize;
    const size = Math.min(width, height);
    const r = size / 2;
    gm(width, height, "transparent")
      .fill("#ffffff")
      .drawCircle(r, r, r, 1)
      .setFormat("png")
      .trim()
      .write(maskPath, (err) => {
        if (err) return callback(err, null);
        gm(pathIn)
          .gravity("Center")
          .crop(size, size)
          .write(tmpPath, (err) => {
            if (err) return callback(err, null);
            gm(tmpPath)
              .composite(tmpPath, maskPath)
              .compose("CopyOpacity")
              .gravity("Center")
              .setFormat("png")
              .toBuffer((err, buffer) => {
                if (err) return callback(err, null);
                fs.unlinkSync(maskPath);
                fs.unlinkSync(tmpPath);
                return callback(null, buffer);
              });
          });
      });
  });
}