ForgeTotal.js 5
nl

Total.js 5

Afbeeldingen schalen en converteren

Total.js 5 · total5@0.0.19 imagesthumbnailsgraphicsmagick Uitgevoerd met Total.js 5

Total.js verwerkt afbeeldingen door GraphicsMagick (gm) of ImageMagick aan te roepen, die op de server geïnstalleerd moeten zijn (brew install graphicsmagick, apt install graphicsmagick). De instelling $imageconverter kiest het programma: gm (standaard), im of magick.

Image.load(filename) (of een Buffer of een stream) geeft een keten terug die de bewerkingen vastlegt en uitvoert wanneer je .save(filename, callback) aanroept. De nuttige bewerkingen zijn resize(width, height) (behoudt de verhoudingen binnen het kader), thumbnail(width, height) (een snelle, kleine voorvertoning), miniature(width, height) (past de afbeelding in het kader en vult de rest met de achtergrond), crop(width, height, x, y), rotate(degrees), grayscale(), blur(radius), quality(percentage) en output('jpg') om het formaat te converteren. Met geüploade bestanden gaat het rechtstreeks: file.image() in een handler voor @upload-routes geeft dezelfde keten terug.

De afmetingen lezen heeft GraphicsMagick niet nodig: Image.measure('png', buffer) geeft { width, height } terug uit de eerste bytes van PNG-, JPG-, GIF- en WebP-gegevens.

$.image(options) is voor een route die op verzoek een afbeelding maakt: ext is het formaat, load(next) levert de bron (een stream of een pad) aan met next(...), image(img) beschrijft de bewerkingen en cache is een naam waaronder het resultaat in tmp/ wordt bewaard voor volgende verzoeken.

controllers/images.js
const Zlib = require('zlib');
const Fs = require('fs');

exports.install = function() {
	ROUTE('GET /resize/', resize);
	ROUTE('GET /thumb/', thumb);
};

// Builds a solid red PNG so that the example needs no file
function makepng(width, height) {

	function chunk(type, data) {
		var length = Buffer.alloc(4);
		length.writeUInt32BE(data.length);
		var body = Buffer.concat([Buffer.from(type), data]);
		var crc = Buffer.alloc(4);
		crc.writeUInt32BE(Zlib.crc32(body));
		return Buffer.concat([length, body, crc]);
	}

	var header = Buffer.alloc(13);
	header.writeUInt32BE(width, 0);
	header.writeUInt32BE(height, 4);
	header[8] = 8; // bit depth
	header[9] = 2; // RGB

	var row = Buffer.alloc(1 + width * 3);
	for (var x = 0; x < width; x++)
		row[1 + x * 3] = 255;

	var raw = Buffer.concat(Array(height).fill(row));

	return Buffer.concat([
		Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
		chunk('IHDR', header),
		chunk('IDAT', Zlib.deflateSync(raw)),
		chunk('IEND', Buffer.alloc(0))
	]);
}

function source() {
	var filename = PATH.tmp('source.png');
	Fs.writeFileSync(filename, makepng(200, 100));
	return filename;
}

function resize($) {

	var target = PATH.tmp('small.jpg');

	Image.load(source()).resize(50, 50).output('jpg').save(target, function(err) {

		if (err) {
			$.invalid(err);
			return;
		}

		var size = Image.measure('jpg', Fs.readFileSync(target));
		$.json({ width: size.width, height: size.height, original: Image.measure('png', Fs.readFileSync(source())) });
	});
}

function thumb($) {
	$.image({
		ext: 'png',
		cache: 'thumb-example',
		load: function(next) {
			next(Fs.createReadStream(source()));
		},
		image: function(img) {
			img.resize(40, 40);
		}
	});
}