total5 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/image.js ADDED
@@ -0,0 +1,777 @@
1
+ // Total.js Images
2
+ // The MIT License
3
+ // Copyright 2016-2023 (c) Peter Širka <petersirka@gmail.com>
4
+
5
+ 'use strict';
6
+
7
+ const D = F.iswindows ? '"' : '\'';
8
+ const SOF = { 0xc0: true, 0xc1: true, 0xc2: true, 0xc3: true, 0xc5: true, 0xc6: true, 0xc7: true, 0xc9: true, 0xca: true, 0xcb: true, 0xcd: true, 0xce: true, 0xcf: true };
9
+ const SPAWN_OPT = { shell: true };
10
+ const CMD_CONVERT = { gm: 'gm', im: 'convert', magick: 'magick' };
11
+ const CMD_CONVERT2 = { gm: 'gm convert', im: 'convert', magick: 'magick' };
12
+ const SUPPORTED = { jpg: 1, png: 1, gif: 1, apng: 1, jpeg: 1, heif: 1, heic: 1, webp: 1, ico: 1 };
13
+
14
+ const REG_SVG = /(width="\d+")+|(height="\d+")+/g;
15
+ const REG_PATH = /\//g;
16
+ const REG_ESCAPE = /'/g;
17
+
18
+ function u16(buf, o) {
19
+ return buf[o] << 8 | buf[o + 1];
20
+ }
21
+
22
+ function u32(buf, o) {
23
+ return buf[o] << 24 | buf[o + 1] << 16 | buf[o + 2] << 8 | buf[o + 3];
24
+ }
25
+
26
+ exports.measureGIF = function(buffer) {
27
+ return { width: buffer.readInt16LE(6), height: buffer.readInt16LE(8) };
28
+ };
29
+
30
+ // MIT
31
+ // Written by TJ Holowaychuk
32
+ // visionmedia
33
+ exports.measureJPG = function(buffer) {
34
+
35
+ var len = buffer.length;
36
+ var o = 0;
37
+
38
+ var jpeg = 0xff == buffer[0] && 0xd8 == buffer[1];
39
+ if (jpeg) {
40
+ o += 2;
41
+ while (o < len) {
42
+ while (0xff != buffer[o]) o++;
43
+ while (0xff == buffer[o]) o++;
44
+ if (SOF[buffer[o]])
45
+ return { width: u16(buffer, o + 6), height: u16(buffer, o + 4) };
46
+ else
47
+ o += u16(buffer, ++o);
48
+
49
+ }
50
+ }
51
+
52
+ return null;
53
+ };
54
+
55
+ // MIT
56
+ // Written by TJ Holowaychuk
57
+ // visionmedia
58
+ exports.measurePNG = function(buffer) {
59
+ return { width: u32(buffer, 16), height: u32(buffer, 16 + 4) };
60
+ };
61
+
62
+ exports.measureSVG = function(buffer) {
63
+
64
+ var match = buffer.toString('utf8').match(REG_SVG);
65
+ if (!match)
66
+ return;
67
+
68
+ var width = 0;
69
+ var height = 0;
70
+
71
+ for (var i = 0, length = match.length; i < length; i++) {
72
+ var value = match[i];
73
+
74
+ if (width > 0 && height > 0)
75
+ break;
76
+
77
+ if (!width && value.startsWith('width="'))
78
+ width = value.parseInt2();
79
+
80
+ if (!height && value.startsWith('height="'))
81
+ height = value.parseInt2();
82
+ }
83
+
84
+ return { width: width, height: height };
85
+ };
86
+
87
+ // image-size
88
+ // Git: https://github.com/image-size/image-size
89
+ // MIT License
90
+ exports.measureWEBP = function(buffer) {
91
+
92
+ var header = buffer.toString('ascii', 12, 16);
93
+
94
+ buffer = buffer.slice(20, 30);
95
+
96
+ if (header === 'VP8X') {
97
+ var extendedheader = buffer[0];
98
+ var start = (extendedheader & 0xc0) === 0;
99
+ var end = (extendedheader & 0x01) === 0;
100
+ if (start && end)
101
+ return { width: 1 + buffer.readUIntLE(7, 3), height: 1 + buffer.readUIntLE(4, 3) };
102
+ }
103
+
104
+ if (header === 'VP8 ' && buffer[0] !== 0x2f)
105
+ return { width: buffer.readInt16LE(8) & 0x3fff, height: buffer.readInt16LE(6) & 0x3fff };
106
+
107
+ var signature = buffer.toString('hex', 3, 6);
108
+ if (header === 'VP8L' && signature !== '9d012a')
109
+ return { width: 1 + (((buffer[2] & 0x3F) << 8) | buffer[1]), height: 1 + (((buffer[4] & 0xF) << 10) | (buffer[3] << 2) | ((buffer[2] & 0xC0) >> 6)) };
110
+ };
111
+
112
+ // image-size
113
+ // Git: https://github.com/image-size/image-size
114
+ // MIT License
115
+ exports.measureBMP = function(buffer) {
116
+ return { width: buffer.readUInt32LE(18), height: Math.abs(buffer.readInt32LE(22)) };
117
+ };
118
+
119
+ // image-size
120
+ // Git: https://github.com/image-size/image-size
121
+ // MIT License
122
+ exports.measurePSD = function(buffer) {
123
+ return { width: buffer.readUInt32BE(18), height: buffer.readUInt32BE(14) };
124
+ };
125
+
126
+ exports.measure = function(type, buffer) {
127
+ switch (type) {
128
+ case '.jpg':
129
+ case '.jpeg':
130
+ case 'jpg':
131
+ case 'jpeg':
132
+ case 'image/jpeg':
133
+ return exports.measureJPG(buffer);
134
+ case '.png':
135
+ case 'png':
136
+ case 'image/png':
137
+ return exports.measurePNG(buffer);
138
+ case '.svg':
139
+ case 'svg':
140
+ case 'image/svg+xml':
141
+ return exports.measureSVG(buffer);
142
+ case '.gif':
143
+ case 'gif':
144
+ case 'image/gif':
145
+ return exports.measureGIF(buffer);
146
+ case '.webp':
147
+ case 'webp':
148
+ case 'image/webp':
149
+ return exports.measureWEBP(buffer);
150
+ case '.psd':
151
+ case 'psd':
152
+ case 'image/vnd.adobe.photoshop':
153
+ return exports.measurePSD(buffer);
154
+ case '.bmp':
155
+ case 'bmp':
156
+ case 'image/bmp':
157
+ return exports.measureBMP(buffer);
158
+ }
159
+ };
160
+
161
+ function Image(filename, cmd, width, height) {
162
+ var t = this;
163
+ var type = typeof(filename);
164
+ t.width = width;
165
+ t.height = height;
166
+ t.builder = [];
167
+ t.filename = type === 'string' ? filename : null;
168
+ t.stream = type === 'object' ? filename : null;
169
+ t.type = type === 'string' ? F.TUtils.getExtension(filename) : 'jpg';
170
+ t.islimit = false;
171
+ t.cmdarg = cmd || F.config.$imageconverter;
172
+ }
173
+
174
+ var ImageProto = Image.prototype;
175
+
176
+ ImageProto.clear = function() {
177
+ var self = this;
178
+ self.builder = [];
179
+ return self;
180
+ };
181
+
182
+ ImageProto.measure = function(callback) {
183
+
184
+ var self = this;
185
+ var index = self.filename.lastIndexOf('.');
186
+
187
+ if (!self.filename) {
188
+ callback(new Error('Measure does not support stream.'));
189
+ return;
190
+ }
191
+
192
+ if (index === -1) {
193
+ callback(new Error('This type of file is not supported.'));
194
+ return;
195
+ }
196
+
197
+ F.stats.performance.open++;
198
+ var extension = self.filename.substring(index).toLowerCase();
199
+ var stream = F.Fs.createReadStream(self.filename, { start: 0, end: (extension === '.jpg' || extension === '.webp') ? 40000 : 24 });
200
+
201
+ stream.on('data', function(buffer) {
202
+
203
+ switch (extension) {
204
+ case '.jpg':
205
+ callback(null, exports.measureJPG(buffer));
206
+ return;
207
+ case '.gif':
208
+ callback(null, exports.measureGIF(buffer));
209
+ return;
210
+ case '.png':
211
+ callback(null, exports.measurePNG(buffer));
212
+ return;
213
+ case '.webp':
214
+ callback(null, exports.measureWEBP(buffer));
215
+ return;
216
+ }
217
+
218
+ callback(new Error('This type of file is not supported.'));
219
+ });
220
+
221
+ stream.on('error', callback);
222
+ return self;
223
+ };
224
+
225
+ ImageProto.$$measure = function() {
226
+ var self = this;
227
+ return function(callback) {
228
+ self.measure(callback);
229
+ };
230
+ };
231
+
232
+ /**
233
+ * Execute commands
234
+ * @param {String} filename
235
+ * @param {Function(err, filename)} callback Optional.
236
+ * @param {Function(stream)} writer A custom stream writer, optional.
237
+ * @return {Image}
238
+ */
239
+ ImageProto.save = function(filename, callback, writer) {
240
+
241
+ var self = this;
242
+
243
+ if (typeof(filename) === 'function') {
244
+ callback = filename;
245
+ filename = null;
246
+ }
247
+
248
+ !self.builder.length && self.minify();
249
+ filename = filename || self.filename || '';
250
+
251
+ var command = self.cmd(self.filename ? self.filename : '-', filename);
252
+
253
+ if (F.isWindows)
254
+ command = command.replace(REG_PATH, '\\');
255
+
256
+ var cmd = F.Child.exec(command, function(err) {
257
+
258
+ // clean up
259
+ cmd.kill();
260
+ cmd = null;
261
+
262
+ self.clear();
263
+
264
+ if (!callback)
265
+ return;
266
+
267
+ if (err) {
268
+ callback(err, false);
269
+ return;
270
+ }
271
+
272
+ var middleware = F.routes.imagemiddleware[self.type];
273
+ if (!middleware)
274
+ return callback(null, true);
275
+
276
+ F.stats.performance.open++;
277
+ var reader = F.Fs.createReadStream(filename);
278
+ var writer = F.Fs.createWriteStream(filename + '_');
279
+
280
+ reader.pipe(middleware()).pipe(writer);
281
+ writer.on('finish', () => F.Fs.rename(filename + '_', filename, () => callback(null, true)));
282
+ });
283
+
284
+ if (self.stream) {
285
+ if (self.stream instanceof Buffer)
286
+ cmd.stdin.end(self.stream);
287
+ else
288
+ self.stream.pipe(cmd.stdin);
289
+ }
290
+
291
+ F.cleanup(cmd.stdin);
292
+ writer && writer(cmd.stdin);
293
+ return self;
294
+ };
295
+
296
+ ImageProto.$$save = function(filename, writer) {
297
+ var self = this;
298
+ return function(callback) {
299
+ self.save(filename, callback, writer);
300
+ };
301
+ };
302
+
303
+ ImageProto.pipe = function(stream, type, options) {
304
+
305
+ var self = this;
306
+
307
+ if (typeof(type) === 'object') {
308
+ options = type;
309
+ type = null;
310
+ }
311
+
312
+ !self.builder.length && self.minify();
313
+
314
+ if (!type || !SUPPORTED[type])
315
+ type = self.type;
316
+
317
+ var cmd = F.Child.spawn(CMD_CONVERT[self.cmdarg], self.arg(self.filename ? wrap(self.filename) : '-', (type ? type + ':' : '') + '-'), SPAWN_OPT);
318
+ cmd.stderr.on('data', stream.emit.bind(stream, 'error'));
319
+ cmd.stdout.on('data', stream.emit.bind(stream, 'data'));
320
+ cmd.stdout.on('end', stream.emit.bind(stream, 'end'));
321
+ cmd.on('error', stream.emit.bind(stream, 'error'));
322
+
323
+ var middleware = F.routes.imagemiddleware[type];
324
+ if (middleware)
325
+ cmd.stdout.pipe(middleware()).pipe(stream, options);
326
+ else
327
+ cmd.stdout.pipe(stream, options);
328
+
329
+ if (self.stream) {
330
+ if (self.stream instanceof Buffer)
331
+ cmd.stdin.end(self.stream);
332
+ else
333
+ self.stream.pipe(cmd.stdin);
334
+ }
335
+
336
+ return self;
337
+ };
338
+
339
+ /**
340
+ * Create a stream
341
+ * @param {String} type File type (png, jpg, gif)
342
+ * @param {Function(stream)} writer A custom stream writer.
343
+ * @return {ReadStream}
344
+ */
345
+ ImageProto.stream = function(type, writer) {
346
+
347
+ var self = this;
348
+
349
+ !self.builder.length && self.minify();
350
+
351
+ if (!type || !SUPPORTED[type])
352
+ type = self.type;
353
+
354
+ F.stats.performance.open++;
355
+ var cmd = F.Child.spawn(CMD_CONVERT[self.cmdarg], self.arg(self.filename ? wrap(self.filename) : '-', (type ? type + ':' : '') + '-'), SPAWN_OPT);
356
+ if (self.stream) {
357
+ if (self.stream instanceof Buffer)
358
+ cmd.stdin.end(self.stream);
359
+ else
360
+ self.stream.pipe(cmd.stdin);
361
+ }
362
+
363
+ writer && writer(cmd.stdin);
364
+ var middleware = F.routes.imagemiddleware[type];
365
+ return middleware ? cmd.stdout.pipe(middleware()) : cmd.stdout;
366
+ };
367
+
368
+ ImageProto.cmd = function(filenameFrom, filenameTo) {
369
+
370
+ var self = this;
371
+ var cmd = '';
372
+
373
+ if (!self.islimit) {
374
+ var tmp = F.config.$imagememory;
375
+ if (tmp) {
376
+ self.limit('memory', (1500 / 100) * tmp);
377
+ self.limit('map', (3000 / 100) * tmp);
378
+ }
379
+ }
380
+
381
+ self.builder.sort(sort);
382
+
383
+ var length = self.builder.length;
384
+ for (var i = 0; i < length; i++)
385
+ cmd += (cmd ? ' ' : '') + self.builder[i].cmd;
386
+
387
+ return CMD_CONVERT2[self.cmdarg] + wrap(filenameFrom, true) + ' ' + cmd + wrap(filenameTo, true);
388
+ };
389
+
390
+ function sort(a, b) {
391
+ return a.priority > b.priority ? 1 : -1;
392
+ }
393
+
394
+ ImageProto.arg = function(first, last) {
395
+
396
+ var self = this;
397
+ var arr = [];
398
+
399
+ if (self.cmdarg === 'gm')
400
+ arr.push('convert');
401
+
402
+ first && arr.push(first);
403
+
404
+ if (!self.islimit) {
405
+ let tmp = F.config.$imagememory;
406
+ if (tmp) {
407
+ self.limit('memory', (1500 / 100) * tmp);
408
+ self.limit('map', (3000 / 100) * tmp);
409
+ }
410
+ }
411
+
412
+ self.builder.sort(sort);
413
+
414
+ for (let o of self.builder) {
415
+ let index = o.cmd.indexOf(' ');
416
+ if (index === -1) {
417
+ arr.push(o.cmd);
418
+ } else {
419
+ arr.push(o.cmd.substring(0, index));
420
+ arr.push(o.cmd.substring(index + 1).replace(/"/g, ''));
421
+ }
422
+ }
423
+
424
+ last && arr.push(last);
425
+ return arr;
426
+ };
427
+
428
+ ImageProto.identify = function(callback) {
429
+ var self = this;
430
+ F.Child.exec((self.cmdarg === 'gm' ? 'gm ' : '') + 'identify' + wrap(self.filename, true), function(err, stdout) {
431
+
432
+ if (err) {
433
+ callback(err, null);
434
+ return;
435
+ }
436
+
437
+ var arr = stdout.split(' ');
438
+ var size = arr[2].split('x');
439
+ var obj = { type: arr[1], width: F.TUtils.parseInt(size[0]), height: F.TUtils.parseInt(size[1]) };
440
+ callback(null, obj);
441
+ });
442
+
443
+ return self;
444
+ };
445
+
446
+ ImageProto.$$identify = function() {
447
+ var self = this;
448
+ return function(callback) {
449
+ self.identify(callback);
450
+ };
451
+ };
452
+
453
+ ImageProto.push = function(key, value, priority, encode) {
454
+ var self = this;
455
+ var cmd = key;
456
+
457
+ if (value != null) {
458
+ if (encode && typeof(value) === 'string')
459
+ cmd += ' ' + D + value.replace(REG_ESCAPE, '') + D;
460
+ else
461
+ cmd += ' ' + value;
462
+ }
463
+
464
+ var obj = F.temporary.images[cmd];
465
+ if (obj) {
466
+ obj.priority = priority;
467
+ self.builder.push(obj);
468
+ } else {
469
+ F.temporary.images[cmd] = { cmd: cmd, priority: priority };
470
+ self.builder.push(F.temporary.images[cmd]);
471
+ }
472
+
473
+ return self;
474
+ };
475
+
476
+ ImageProto.output = function(type) {
477
+ var self = this;
478
+ if (type[0] === '.')
479
+ type = type.substring(1);
480
+ self.type = type;
481
+ return self;
482
+ };
483
+
484
+ ImageProto.resize = function(w, h, options) {
485
+ options = options || '';
486
+
487
+ var self = this;
488
+ var size = '';
489
+
490
+ if (w && h)
491
+ size = w + 'x' + h;
492
+ else if (w && !h)
493
+ size = w + 'x';
494
+ else if (!w && h)
495
+ size = 'x' + h;
496
+
497
+ return self.push('-resize', size + options, 1, true);
498
+ };
499
+
500
+ ImageProto.thumbnail = function(w, h, options) {
501
+ options = options || '';
502
+
503
+ var self = this;
504
+ var size = '';
505
+
506
+ if (w && h)
507
+ size = w + 'x' + h;
508
+ else if (w && !h)
509
+ size = w;
510
+ else if (!w && h)
511
+ size = 'x' + h;
512
+
513
+ return self.push('-thumbnail', size + options, 1, true);
514
+ };
515
+
516
+ ImageProto.geometry = function(w, h, options) {
517
+ options = options || '';
518
+
519
+ var self = this;
520
+ var size = '';
521
+
522
+ if (w && h)
523
+ size = w + 'x' + h;
524
+ else if (w && !h)
525
+ size = w;
526
+ else if (!w && h)
527
+ size = 'x' + h;
528
+
529
+ return self.push('-geometry', size + options, 1, true);
530
+ };
531
+
532
+
533
+ ImageProto.filter = function(type) {
534
+ return this.push('-filter', type, 1, true);
535
+ };
536
+
537
+ ImageProto.trim = function() {
538
+ return this.push('-trim +repage', 1);
539
+ };
540
+
541
+ ImageProto.limit = function(type, value) {
542
+ this.islimit = true;
543
+ return this.push('-limit', type + ' ' + value, 1);
544
+ };
545
+
546
+ ImageProto.extent = function(w, h, x, y) {
547
+
548
+ var self = this;
549
+ var size = '';
550
+
551
+ if (w && h)
552
+ size = w + 'x' + h;
553
+ else if (w && !h)
554
+ size = w;
555
+ else if (!w && h)
556
+ size = 'x' + h;
557
+
558
+ if (x || y) {
559
+ !x && (x = 0);
560
+ !y && (y = 0);
561
+ size += (x >= 0 ? '+' : '') + x + (y >= 0 ? '+' : '') + y;
562
+ }
563
+
564
+ return self.push('-extent', size, 4, true);
565
+ };
566
+
567
+ /**
568
+ * Resize picture to miniature (full picture)
569
+ * @param {Number} w
570
+ * @param {Number} h
571
+ * @param {String} color Optional, background color.
572
+ * @param {String} filter Optional, resize filter (default: Box)
573
+ * @return {Image}
574
+ */
575
+ ImageProto.miniature = function(w, h, color, filter) {
576
+ return this.filter(filter || 'Hamming').thumbnail(w, h).background(color ? color : 'white').align('center').extent(w, h);
577
+ };
578
+
579
+ /**
580
+ * Resize picture to center
581
+ * @param {Number} w
582
+ * @param {Number} h
583
+ * @param {String} color Optional, background color.
584
+ * @return {Image}
585
+ */
586
+ ImageProto.resizeCenter = ImageProto.resize_center = function(w, h, color) {
587
+ return this.resize(w, h, '^').background(color ? color : 'white').align('center').crop(w, h);
588
+ };
589
+
590
+ /**
591
+ * Resize picture to align
592
+ * @param {Number} w
593
+ * @param {Number} h
594
+ * @param {String} align (top, center, bottom)
595
+ * @param {String} color Optional, background color.
596
+ * @return {Image}
597
+ */
598
+ ImageProto.resizeAlign = ImageProto.resize_align = function(w, h, align, color) {
599
+ return this.resize(w, h, '^').background(color ? color : 'white').align(align || 'center').crop(w, h);
600
+ };
601
+
602
+ ImageProto.scale = function(w, h, options) {
603
+ options = options || '';
604
+
605
+ var self = this;
606
+ var size = '';
607
+
608
+ if (w && h)
609
+ size = w + 'x' + h;
610
+ else if (w && !h)
611
+ size = w;
612
+ else if (!w && h)
613
+ size = 'x' + h;
614
+
615
+ return self.push('-scale', size + options, 1, true);
616
+ };
617
+
618
+ ImageProto.crop = function(w, h, x, y) {
619
+ return this.push('-crop', w + 'x' + h + '+' + (x || 0) + '+' + (y || 0), 4, true);
620
+ };
621
+
622
+ ImageProto.quality = function(percentage) {
623
+ return this.push('-quality', percentage || 80, 5, true);
624
+ };
625
+
626
+ ImageProto.align = function(type) {
627
+
628
+ var output;
629
+
630
+ switch (type) {
631
+ case 'left top':
632
+ case 'top left':
633
+ output = 'NorthWest';
634
+ break;
635
+ case 'left bottom':
636
+ case 'bottom left':
637
+ output = 'SouthWest';
638
+ break;
639
+ case 'right top':
640
+ case 'top right':
641
+ output = 'NorthEast';
642
+ break;
643
+ case 'right bottom':
644
+ case 'bottom right':
645
+ output = 'SouthEast';
646
+ break;
647
+ case 'left center':
648
+ case 'center left':
649
+ case 'left':
650
+ output = 'West';
651
+ break;
652
+ case 'right center':
653
+ case 'center right':
654
+ case 'right':
655
+ output = 'East';
656
+ break;
657
+ case 'bottom center':
658
+ case 'center bottom':
659
+ case 'bottom':
660
+ output = 'South';
661
+ break;
662
+ case 'top center':
663
+ case 'center top':
664
+ case 'top':
665
+ output = 'North';
666
+ break;
667
+ case 'center center':
668
+ case 'center':
669
+ case 'middle':
670
+ output = 'Center';
671
+ break;
672
+ default:
673
+ output = type;
674
+ break;
675
+ }
676
+
677
+ output && this.push('-gravity', output, 3, true);
678
+ return this;
679
+ };
680
+
681
+ ImageProto.gravity = function(type) {
682
+ return this.align(type);
683
+ };
684
+
685
+ ImageProto.blur = function(radius) {
686
+ return this.push('-blur', radius, 10, true);
687
+ };
688
+
689
+ ImageProto.normalize = function() {
690
+ return this.push('-normalize', null, 10);
691
+ };
692
+
693
+ ImageProto.rotate = function(deg) {
694
+ return this.push('-rotate', deg || 0, 8, true);
695
+ };
696
+
697
+ ImageProto.flip = function() {
698
+ return this.push('-flip', null, 10);
699
+ };
700
+
701
+ ImageProto.flop = function() {
702
+ return this.push('-flop', null, 10);
703
+ };
704
+
705
+ ImageProto.define = function(value) {
706
+ return this.push('-define', value, 10, true);
707
+ };
708
+
709
+ ImageProto.minify = function() {
710
+ return this.push('+profile', '*', null, 10, true);
711
+ };
712
+
713
+ ImageProto.grayscale = function() {
714
+ return this.push('-colorspace', 'Gray', 10, true);
715
+ };
716
+
717
+ ImageProto.bitdepth = function(value) {
718
+ return this.push('-depth', value, 10, true);
719
+ };
720
+
721
+ ImageProto.colors = function(value) {
722
+ return this.push('-colors', value, 10, true);
723
+ };
724
+
725
+ ImageProto.background = function(color) {
726
+ return this.push('-background', color, 2, true).push('-extent 0x0', null, 2);
727
+ };
728
+
729
+ ImageProto.fill = function(color) {
730
+ return this.push('-fill', color, 2, true);
731
+ };
732
+
733
+ ImageProto.sepia = function() {
734
+ return this.push('-modulate', '115,0,100', 4).push('-colorize', '7,21,50', 5);
735
+ };
736
+
737
+ ImageProto.watermark = function(filename, x, y, w, h) {
738
+ return this.push('-draw', 'image over {1},{2} {3},{4} {5}{0}{5}'.format(filename, x || 0, y || 0, w || 0, h || 0, D), 6, true);
739
+ };
740
+
741
+ ImageProto.make = function(fn) {
742
+ fn.call(this, this);
743
+ return this;
744
+ };
745
+
746
+ ImageProto.command = function(key, value, priority, esc) {
747
+
748
+ if (priority === true) {
749
+ priority = 0;
750
+ esc = true;
751
+ }
752
+
753
+ return this.push(key, value, priority || 10, esc);
754
+ };
755
+
756
+ function wrap(command, empty) {
757
+ return (empty ? ' ' : '') + (command === '-' ? command : (D + command.replace(REG_ESCAPE, '') + D));
758
+ }
759
+
760
+ exports.Image = Image;
761
+ exports.Picture = Image;
762
+
763
+ exports.init = function(filename, cmd, width, height) {
764
+ return new Image(filename, cmd, width, height);
765
+ };
766
+
767
+ exports.load = function(filename, cmd, width, height) {
768
+ return new Image(filename, cmd, width, height);
769
+ };
770
+
771
+ exports.middleware = function(type, fn) {
772
+ if (type[0] === '.')
773
+ type = type.substring(1);
774
+ F.routes.imagemiddleware[type] = fn;
775
+ };
776
+
777
+ global.Image = exports;