pica 7.0.0 → 9.0.0

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/dist/pica.js CHANGED
@@ -13,8 +13,6 @@ https://github.com/nodeca/pica
13
13
  //
14
14
  'use strict';
15
15
 
16
- var inherits = _dereq_('inherits');
17
-
18
16
  var Multimath = _dereq_('multimath');
19
17
 
20
18
  var mm_unsharp_mask = _dereq_('./mm_unsharp_mask');
@@ -37,7 +35,8 @@ function MathLib(requested_features) {
37
35
  this.use(mm_resize);
38
36
  }
39
37
 
40
- inherits(MathLib, Multimath);
38
+ MathLib.prototype = Object.create(Multimath.prototype);
39
+ MathLib.prototype.constructor = MathLib;
41
40
 
42
41
  MathLib.prototype.resizeAndUnsharp = function resizeAndUnsharp(options, cache) {
43
42
  var result = this.resize(options, cache);
@@ -51,7 +50,7 @@ MathLib.prototype.resizeAndUnsharp = function resizeAndUnsharp(options, cache) {
51
50
 
52
51
  module.exports = MathLib;
53
52
 
54
- },{"./mm_resize":4,"./mm_unsharp_mask":9,"inherits":19,"multimath":20}],2:[function(_dereq_,module,exports){
53
+ },{"./mm_resize":4,"./mm_unsharp_mask":9,"multimath":19}],2:[function(_dereq_,module,exports){
55
54
  // Resize convolvers, pure JS implementation
56
55
  //
57
56
  'use strict'; // Precision of fixed FP values
@@ -59,18 +58,23 @@ module.exports = MathLib;
59
58
 
60
59
  function clampTo8(i) {
61
60
  return i < 0 ? 0 : i > 255 ? 255 : i;
62
- } // Convolve image in horizontal directions and transpose output. In theory,
63
- // transpose allow:
61
+ }
62
+
63
+ function clampNegative(i) {
64
+ return i >= 0 ? i : 0;
65
+ } // Convolve image data in horizontal direction. Can be used for:
66
+ //
67
+ // 1. bitmap with premultiplied alpha
68
+ // 2. bitmap without alpha (all values 255)
64
69
  //
65
- // - use the same convolver for both passes (this fails due different
66
- // types of input array and temporary buffer)
67
- // - making vertical pass by horisonltal lines inprove CPU cache use.
70
+ // Notes:
68
71
  //
69
- // But in real life this doesn't work :)
72
+ // - output is transposed
73
+ // - output resolution is ~15 bits per channel(for better precision).
70
74
  //
71
75
 
72
76
 
73
- function convolveHorizontally(src, dest, srcW, srcH, destW, filters) {
77
+ function convolveHor(src, dest, srcW, srcH, destW, filters) {
74
78
  var r, g, b, a;
75
79
  var filterPtr, filterShift, filterSize;
76
80
  var srcPtr, srcY, destX, filterVal;
@@ -96,39 +100,26 @@ function convolveHorizontally(src, dest, srcW, srcH, destW, filters) {
96
100
  g = g + filterVal * src[srcPtr + 1] | 0;
97
101
  r = r + filterVal * src[srcPtr] | 0;
98
102
  srcPtr = srcPtr + 4 | 0;
99
- } // Bring this value back in range. All of the filter scaling factors
100
- // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
101
- //
102
- // (!) Add 1/2 of value before clamping to get proper rounding. In other
103
- // case brightness loss will be noticeable if you resize image with white
104
- // border and place it on white background.
103
+ } // Store 15 bits between passes for better precision
104
+ // Instead of shift to 14 (FIXED_FRAC_BITS), shift to 7 only
105
105
  //
106
106
 
107
107
 
108
- dest[destOffset + 3] = clampTo8(a + (1 << 13) >> 14
109
- /*FIXED_FRAC_BITS*/
110
- );
111
- dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14
112
- /*FIXED_FRAC_BITS*/
113
- );
114
- dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14
115
- /*FIXED_FRAC_BITS*/
116
- );
117
- dest[destOffset] = clampTo8(r + (1 << 13) >> 14
118
- /*FIXED_FRAC_BITS*/
119
- );
108
+ dest[destOffset + 3] = clampNegative(a >> 7);
109
+ dest[destOffset + 2] = clampNegative(b >> 7);
110
+ dest[destOffset + 1] = clampNegative(g >> 7);
111
+ dest[destOffset] = clampNegative(r >> 7);
120
112
  destOffset = destOffset + srcH * 4 | 0;
121
113
  }
122
114
 
123
115
  destOffset = (srcY + 1) * 4 | 0;
124
116
  srcOffset = (srcY + 1) * srcW * 4 | 0;
125
117
  }
126
- } // Technically, convolvers are the same. But input array and temporary
127
- // buffer can be of different type (especially, in old browsers). So,
128
- // keep code in separate functions to avoid deoptimizations & speed loss.
118
+ } // Supplementary method for `convolveHor()`
119
+ //
129
120
 
130
121
 
131
- function convolveVertically(src, dest, srcW, srcH, destW, filters) {
122
+ function convolveVert(src, dest, srcW, srcH, destW, filters) {
132
123
  var r, g, b, a;
133
124
  var filterPtr, filterShift, filterSize;
134
125
  var srcPtr, srcY, destX, filterVal;
@@ -154,27 +145,134 @@ function convolveVertically(src, dest, srcW, srcH, destW, filters) {
154
145
  g = g + filterVal * src[srcPtr + 1] | 0;
155
146
  r = r + filterVal * src[srcPtr] | 0;
156
147
  srcPtr = srcPtr + 4 | 0;
157
- } // Bring this value back in range. All of the filter scaling factors
158
- // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
148
+ } // Sync with premultiplied version for exact result match
149
+
150
+
151
+ r >>= 7;
152
+ g >>= 7;
153
+ b >>= 7;
154
+ a >>= 7; // Bring this value back in range + round result.
155
+ //
156
+
157
+ dest[destOffset + 3] = clampTo8(a + (1 << 13) >> 14);
158
+ dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14);
159
+ dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14);
160
+ dest[destOffset] = clampTo8(r + (1 << 13) >> 14);
161
+ destOffset = destOffset + srcH * 4 | 0;
162
+ }
163
+
164
+ destOffset = (srcY + 1) * 4 | 0;
165
+ srcOffset = (srcY + 1) * srcW * 4 | 0;
166
+ }
167
+ } // Premultiply & convolve image data in horizontal direction. Can be used for:
168
+ //
169
+ // - Any bitmap data, extracted with `.getImageData()` method (with
170
+ // non-premultiplied alpha)
171
+ //
172
+ // For images without alpha channel this method is slower than `convolveHor()`
173
+ //
174
+
175
+
176
+ function convolveHorWithPre(src, dest, srcW, srcH, destW, filters) {
177
+ var r, g, b, a, alpha;
178
+ var filterPtr, filterShift, filterSize;
179
+ var srcPtr, srcY, destX, filterVal;
180
+ var srcOffset = 0,
181
+ destOffset = 0; // For each row
182
+
183
+ for (srcY = 0; srcY < srcH; srcY++) {
184
+ filterPtr = 0; // Apply precomputed filters to each destination row point
185
+
186
+ for (destX = 0; destX < destW; destX++) {
187
+ // Get the filter that determines the current output pixel.
188
+ filterShift = filters[filterPtr++];
189
+ filterSize = filters[filterPtr++];
190
+ srcPtr = srcOffset + filterShift * 4 | 0;
191
+ r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a
192
+
193
+ for (; filterSize > 0; filterSize--) {
194
+ filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10)
195
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
196
+
197
+ alpha = src[srcPtr + 3];
198
+ a = a + filterVal * alpha | 0;
199
+ b = b + filterVal * src[srcPtr + 2] * alpha | 0;
200
+ g = g + filterVal * src[srcPtr + 1] * alpha | 0;
201
+ r = r + filterVal * src[srcPtr] * alpha | 0;
202
+ srcPtr = srcPtr + 4 | 0;
203
+ } // Premultiply is (* alpha / 255).
204
+ // Postpone division for better performance
205
+
206
+
207
+ b = b / 255 | 0;
208
+ g = g / 255 | 0;
209
+ r = r / 255 | 0; // Store 15 bits between passes for better precision
210
+ // Instead of shift to 14 (FIXED_FRAC_BITS), shift to 7 only
159
211
  //
160
- // (!) Add 1/2 of value before clamping to get proper rounding. In other
161
- // case brightness loss will be noticeable if you resize image with white
162
- // border and place it on white background.
212
+
213
+ dest[destOffset + 3] = clampNegative(a >> 7);
214
+ dest[destOffset + 2] = clampNegative(b >> 7);
215
+ dest[destOffset + 1] = clampNegative(g >> 7);
216
+ dest[destOffset] = clampNegative(r >> 7);
217
+ destOffset = destOffset + srcH * 4 | 0;
218
+ }
219
+
220
+ destOffset = (srcY + 1) * 4 | 0;
221
+ srcOffset = (srcY + 1) * srcW * 4 | 0;
222
+ }
223
+ } // Supplementary method for `convolveHorWithPre()`
224
+ //
225
+
226
+
227
+ function convolveVertWithPre(src, dest, srcW, srcH, destW, filters) {
228
+ var r, g, b, a;
229
+ var filterPtr, filterShift, filterSize;
230
+ var srcPtr, srcY, destX, filterVal;
231
+ var srcOffset = 0,
232
+ destOffset = 0; // For each row
233
+
234
+ for (srcY = 0; srcY < srcH; srcY++) {
235
+ filterPtr = 0; // Apply precomputed filters to each destination row point
236
+
237
+ for (destX = 0; destX < destW; destX++) {
238
+ // Get the filter that determines the current output pixel.
239
+ filterShift = filters[filterPtr++];
240
+ filterSize = filters[filterPtr++];
241
+ srcPtr = srcOffset + filterShift * 4 | 0;
242
+ r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a
243
+
244
+ for (; filterSize > 0; filterSize--) {
245
+ filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10)
246
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
247
+
248
+ a = a + filterVal * src[srcPtr + 3] | 0;
249
+ b = b + filterVal * src[srcPtr + 2] | 0;
250
+ g = g + filterVal * src[srcPtr + 1] | 0;
251
+ r = r + filterVal * src[srcPtr] | 0;
252
+ srcPtr = srcPtr + 4 | 0;
253
+ } // Downscale to leave room for un-premultiply
254
+
255
+
256
+ r >>= 7;
257
+ g >>= 7;
258
+ b >>= 7;
259
+ a >>= 7; // Un-premultiply
260
+
261
+ a = clampTo8(a + (1 << 13) >> 14);
262
+
263
+ if (a > 0) {
264
+ r = r * 255 / a | 0;
265
+ g = g * 255 / a | 0;
266
+ b = b * 255 / a | 0;
267
+ } // Bring this value back in range + round result.
268
+ // Shift value = FIXED_FRAC_BITS + 7
163
269
  //
164
270
 
165
271
 
166
- dest[destOffset + 3] = clampTo8(a + (1 << 13) >> 14
167
- /*FIXED_FRAC_BITS*/
168
- );
169
- dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14
170
- /*FIXED_FRAC_BITS*/
171
- );
172
- dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14
173
- /*FIXED_FRAC_BITS*/
174
- );
175
- dest[destOffset] = clampTo8(r + (1 << 13) >> 14
176
- /*FIXED_FRAC_BITS*/
177
- );
272
+ dest[destOffset + 3] = a;
273
+ dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14);
274
+ dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14);
275
+ dest[destOffset] = clampTo8(r + (1 << 13) >> 14);
178
276
  destOffset = destOffset + srcH * 4 | 0;
179
277
  }
180
278
 
@@ -184,8 +282,10 @@ function convolveVertically(src, dest, srcW, srcH, destW, filters) {
184
282
  }
185
283
 
186
284
  module.exports = {
187
- convolveHorizontally: convolveHorizontally,
188
- convolveVertically: convolveVertically
285
+ convolveHor: convolveHor,
286
+ convolveVert: convolveVert,
287
+ convolveHorWithPre: convolveHorWithPre,
288
+ convolveVertWithPre: convolveVertWithPre
189
289
  };
190
290
 
191
291
  },{}],3:[function(_dereq_,module,exports){
@@ -194,7 +294,7 @@ module.exports = {
194
294
  'use strict';
195
295
  /* eslint-disable max-len */
196
296
 
197
- module.exports = 'AGFzbQEAAAAADAZkeWxpbmsAAAAAAAEXA2AAAGAGf39/f39/AGAHf39/f39/fwACDwEDZW52Bm1lbW9yeQIAAAMEAwABAgYGAX8AQQALB1cFEV9fd2FzbV9jYWxsX2N0b3JzAAAIY29udm9sdmUAAQpjb252b2x2ZUhWAAIMX19kc29faGFuZGxlAwAYX193YXNtX2FwcGx5X2RhdGFfcmVsb2NzAAAK7AMDAwABC8YDAQ9/AkAgA0UNACAERQ0AA0AgDCENQQAhE0EAIQcDQCAHQQJqIQYCfyAHQQF0IAVqIgcuAQIiFEUEQEGAwAAhCEGAwAAhCUGAwAAhCkGAwAAhCyAGDAELIBIgBy4BAGohCEEAIQsgFCEHQQAhDiAGIQlBACEPQQAhEANAIAUgCUEBdGouAQAiESAAIAhBAnRqKAIAIgpBGHZsIBBqIRAgCkH/AXEgEWwgC2ohCyAKQRB2Qf8BcSARbCAPaiEPIApBCHZB/wFxIBFsIA5qIQ4gCEEBaiEIIAlBAWohCSAHQQFrIgcNAAsgC0GAQGshCCAOQYBAayEJIA9BgEBrIQogEEGAQGshCyAGIBRqCyEHIAEgDUECdGogCUEOdSIGQf8BIAZB/wFIGyIGQQAgBkEAShtBCHRBgP4DcSAKQQ51IgZB/wEgBkH/AUgbIgZBACAGQQBKG0EQdEGAgPwHcSALQQ51IgZB/wEgBkH/AUgbIgZBACAGQQBKG0EYdHJyIAhBDnUiBkH/ASAGQf8BSBsiBkEAIAZBAEobcjYCACADIA1qIQ0gE0EBaiITIARHDQALIAxBAWoiDCACbCESIAMgDEcNAAsLCx4AQQAgAiADIAQgBSAAEAEgAkEAIAQgBSAGIAEQAQs=';
297
+ module.exports = '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';
198
298
 
199
299
  },{}],4:[function(_dereq_,module,exports){
200
300
  'use strict';
@@ -211,9 +311,23 @@ module.exports = {
211
311
 
212
312
  var createFilters = _dereq_('./resize_filter_gen');
213
313
 
214
- var convolveHorizontally = _dereq_('./convolve').convolveHorizontally;
314
+ var _require = _dereq_('./convolve'),
315
+ convolveHor = _require.convolveHor,
316
+ convolveVert = _require.convolveVert,
317
+ convolveHorWithPre = _require.convolveHorWithPre,
318
+ convolveVertWithPre = _require.convolveVertWithPre;
215
319
 
216
- var convolveVertically = _dereq_('./convolve').convolveVertically;
320
+ function hasAlpha(src, width, height) {
321
+ var ptr = 3,
322
+ len = width * height * 4 | 0;
323
+
324
+ while (ptr < len) {
325
+ if (src[ptr] !== 255) return true;
326
+ ptr = ptr + 4 | 0;
327
+ }
328
+
329
+ return false;
330
+ }
217
331
 
218
332
  function resetAlpha(dst, width, height) {
219
333
  var ptr = 3,
@@ -236,20 +350,20 @@ module.exports = function resize(options) {
236
350
  var offsetX = options.offsetX || 0;
237
351
  var offsetY = options.offsetY || 0;
238
352
  var dest = options.dest || new Uint8Array(destW * destH * 4);
239
- var quality = typeof options.quality === 'undefined' ? 3 : options.quality;
240
- var alpha = options.alpha || false;
241
- var filtersX = createFilters(quality, srcW, destW, scaleX, offsetX),
242
- filtersY = createFilters(quality, srcH, destH, scaleY, offsetY);
243
- var tmp = new Uint8Array(destW * srcH * 4); // To use single function we need src & tmp of the same type.
244
- // But src can be CanvasPixelArray, and tmp - Uint8Array. So, keep
245
- // vertical and horizontal passes separately to avoid deoptimization.
246
-
247
- convolveHorizontally(src, tmp, srcW, srcH, destW, filtersX);
248
- convolveVertically(tmp, dest, srcH, destW, destH, filtersY); // That's faster than doing checks in convolver.
249
- // !!! Note, canvas data is not premultipled. We don't need other
250
- // alpha corrections.
251
-
252
- if (!alpha) resetAlpha(dest, destW, destH);
353
+ var filter = typeof options.filter === 'undefined' ? 'mks2013' : options.filter;
354
+ var filtersX = createFilters(filter, srcW, destW, scaleX, offsetX),
355
+ filtersY = createFilters(filter, srcH, destH, scaleY, offsetY);
356
+ var tmp = new Uint16Array(destW * srcH * 4); // Autodetect if alpha channel exists, and use appropriate method
357
+
358
+ if (hasAlpha(src, srcW, srcH)) {
359
+ convolveHorWithPre(src, tmp, srcW, srcH, destW, filtersX);
360
+ convolveVertWithPre(tmp, dest, srcH, destW, destH, filtersY);
361
+ } else {
362
+ convolveHor(src, tmp, srcW, srcH, destW, filtersX);
363
+ convolveVert(tmp, dest, srcH, destW, destH, filtersY);
364
+ resetAlpha(dest, destW, destH);
365
+ }
366
+
253
367
  return dest;
254
368
  };
255
369
 
@@ -274,13 +388,13 @@ function toFixedPoint(num) {
274
388
  return Math.round(num * ((1 << FIXED_FRAC_BITS) - 1));
275
389
  }
276
390
 
277
- module.exports = function resizeFilterGen(quality, srcSize, destSize, scale, offset) {
278
- var filterFunction = FILTER_INFO[quality].filter;
391
+ module.exports = function resizeFilterGen(filter, srcSize, destSize, scale, offset) {
392
+ var filterFunction = FILTER_INFO.filter[filter].fn;
279
393
  var scaleInverted = 1.0 / scale;
280
394
  var scaleClamped = Math.min(1.0, scale); // For upscale
281
395
  // Filter window (averaging interval), scaled to src image
282
396
 
283
- var srcWindow = FILTER_INFO[quality].win / scaleClamped;
397
+ var srcWindow = FILTER_INFO.filter[filter].win / scaleClamped;
284
398
  var destPixel, srcPixel, srcFirst, srcLast, filterElementSize, floatFilter, fxpFilter, total, pxl, idx, floatVal, filterTotal, filterVal;
285
399
  var leftNotEmpty, rightNotEmpty, filterShift, filterSize;
286
400
  var maxFilterElementSize = Math.floor((srcWindow + 1) * 2);
@@ -370,64 +484,121 @@ module.exports = function resizeFilterGen(quality, srcSize, destSize, scale, off
370
484
  //
371
485
  'use strict';
372
486
 
373
- module.exports = [{
374
- // Nearest neibor (Box)
375
- win: 0.5,
376
- filter: function filter(x) {
377
- return x >= -0.5 && x < 0.5 ? 1.0 : 0.0;
378
- }
379
- }, {
380
- // Hamming
381
- win: 1.0,
382
- filter: function filter(x) {
383
- if (x <= -1.0 || x >= 1.0) {
384
- return 0.0;
487
+ var filter = {
488
+ // Nearest neibor
489
+ box: {
490
+ win: 0.5,
491
+ fn: function fn(x) {
492
+ if (x < 0) x = -x;
493
+ return x < 0.5 ? 1.0 : 0.0;
385
494
  }
495
+ },
496
+ // // Hamming
497
+ hamming: {
498
+ win: 1.0,
499
+ fn: function fn(x) {
500
+ if (x < 0) x = -x;
501
+
502
+ if (x >= 1.0) {
503
+ return 0.0;
504
+ }
386
505
 
387
- if (x > -1.19209290E-07 && x < 1.19209290E-07) {
388
- return 1.0;
389
- }
506
+ if (x < 1.19209290E-07) {
507
+ return 1.0;
508
+ }
390
509
 
391
- var xpi = x * Math.PI;
392
- return Math.sin(xpi) / xpi * (0.54 + 0.46 * Math.cos(xpi / 1.0));
393
- }
394
- }, {
395
- // Lanczos, win = 2
396
- win: 2.0,
397
- filter: function filter(x) {
398
- if (x <= -2.0 || x >= 2.0) {
399
- return 0.0;
510
+ var xpi = x * Math.PI;
511
+ return Math.sin(xpi) / xpi * (0.54 + 0.46 * Math.cos(xpi / 1.0));
400
512
  }
513
+ },
514
+ // Lanczos, win = 2
515
+ lanczos2: {
516
+ win: 2.0,
517
+ fn: function fn(x) {
518
+ if (x < 0) x = -x;
401
519
 
402
- if (x > -1.19209290E-07 && x < 1.19209290E-07) {
403
- return 1.0;
404
- }
520
+ if (x >= 2.0) {
521
+ return 0.0;
522
+ }
405
523
 
406
- var xpi = x * Math.PI;
407
- return Math.sin(xpi) / xpi * Math.sin(xpi / 2.0) / (xpi / 2.0);
408
- }
409
- }, {
410
- // Lanczos, win = 3
411
- win: 3.0,
412
- filter: function filter(x) {
413
- if (x <= -3.0 || x >= 3.0) {
414
- return 0.0;
524
+ if (x < 1.19209290E-07) {
525
+ return 1.0;
526
+ }
527
+
528
+ var xpi = x * Math.PI;
529
+ return Math.sin(xpi) / xpi * Math.sin(xpi / 2.0) / (xpi / 2.0);
415
530
  }
531
+ },
532
+ // Lanczos, win = 3
533
+ lanczos3: {
534
+ win: 3.0,
535
+ fn: function fn(x) {
536
+ if (x < 0) x = -x;
537
+
538
+ if (x >= 3.0) {
539
+ return 0.0;
540
+ }
541
+
542
+ if (x < 1.19209290E-07) {
543
+ return 1.0;
544
+ }
416
545
 
417
- if (x > -1.19209290E-07 && x < 1.19209290E-07) {
418
- return 1.0;
546
+ var xpi = x * Math.PI;
547
+ return Math.sin(xpi) / xpi * Math.sin(xpi / 3.0) / (xpi / 3.0);
419
548
  }
549
+ },
550
+ // Magic Kernel Sharp 2013, win = 2.5
551
+ // http://johncostella.com/magic/
552
+ mks2013: {
553
+ win: 2.5,
554
+ fn: function fn(x) {
555
+ if (x < 0) x = -x;
556
+
557
+ if (x >= 2.5) {
558
+ return 0.0;
559
+ }
560
+
561
+ if (x >= 1.5) {
562
+ return -0.125 * (x - 2.5) * (x - 2.5);
563
+ }
420
564
 
421
- var xpi = x * Math.PI;
422
- return Math.sin(xpi) / xpi * Math.sin(xpi / 3.0) / (xpi / 3.0);
565
+ if (x >= 0.5) {
566
+ return 0.25 * (4 * x * x - 11 * x + 7);
567
+ }
568
+
569
+ return 1.0625 - 1.75 * x * x;
570
+ }
423
571
  }
424
- }];
572
+ };
573
+ module.exports = {
574
+ filter: filter,
575
+ // Legacy mapping
576
+ f2q: {
577
+ box: 0,
578
+ hamming: 1,
579
+ lanczos2: 2,
580
+ lanczos3: 3
581
+ },
582
+ q2f: ['box', 'hamming', 'lanczos2', 'lanczos3']
583
+ };
425
584
 
426
585
  },{}],8:[function(_dereq_,module,exports){
427
586
  'use strict';
428
587
 
429
588
  var createFilters = _dereq_('./resize_filter_gen');
430
589
 
590
+ function hasAlpha(src, width, height) {
591
+ var ptr = 3,
592
+ len = width * height * 4 | 0;
593
+
594
+ while (ptr < len) {
595
+ if (src[ptr] !== 255) return true;
596
+ ptr = ptr + 4 | 0;
597
+ }
598
+
599
+ return false;
600
+ }
601
+
431
602
  function resetAlpha(dst, width, height) {
432
603
  var ptr = 3,
433
604
  len = width * height * 4 | 0;
@@ -472,16 +643,18 @@ module.exports = function resize_wasm(options) {
472
643
  var offsetX = options.offsetX || 0.0;
473
644
  var offsetY = options.offsetY || 0.0;
474
645
  var dest = options.dest || new Uint8Array(destW * destH * 4);
475
- var quality = typeof options.quality === 'undefined' ? 3 : options.quality;
476
- var alpha = options.alpha || false;
477
- var filtersX = createFilters(quality, srcW, destW, scaleX, offsetX),
478
- filtersY = createFilters(quality, srcH, destH, scaleY, offsetY); // destination is 0 too.
646
+ var filter = typeof options.filter === 'undefined' ? 'mks2013' : options.filter;
647
+ var filtersX = createFilters(filter, srcW, destW, scaleX, offsetX),
648
+ filtersY = createFilters(filter, srcH, destH, scaleY, offsetY); // destination is 0 too.
649
+
650
+ var src_offset = 0;
651
+ var src_size = Math.max(src.byteLength, dest.byteLength); // buffer between convolve passes
479
652
 
480
- var src_offset = 0; // buffer between convolve passes
653
+ var tmp_offset = this.__align(src_offset + src_size);
481
654
 
482
- var tmp_offset = this.__align(src_offset + Math.max(src.byteLength, dest.byteLength));
655
+ var tmp_size = srcH * destW * 4 * 2; // 2 bytes per channel
483
656
 
484
- var filtersX_offset = this.__align(tmp_offset + srcH * destW * 4);
657
+ var filtersX_offset = this.__align(tmp_offset + tmp_size);
485
658
 
486
659
  var filtersY_offset = this.__align(filtersX_offset + filtersX.byteLength);
487
660
 
@@ -500,22 +673,24 @@ module.exports = function resize_wasm(options) {
500
673
  // speed difference is not significant vs direct .set()
501
674
 
502
675
  copyInt16asLE(filtersX, mem, filtersX_offset);
503
- copyInt16asLE(filtersY, mem, filtersY_offset); //
504
- // Now call webassembly method
676
+ copyInt16asLE(filtersY, mem, filtersY_offset); // Now call webassembly method
505
677
  // emsdk does method names with '_'
506
678
 
507
679
  var fn = instance.exports.convolveHV || instance.exports._convolveHV;
508
- fn(filtersX_offset, filtersY_offset, tmp_offset, srcW, srcH, destW, destH); //
680
+
681
+ if (hasAlpha(src, srcW, srcH)) {
682
+ fn(filtersX_offset, filtersY_offset, tmp_offset, srcW, srcH, destW, destH, 1);
683
+ } else {
684
+ fn(filtersX_offset, filtersY_offset, tmp_offset, srcW, srcH, destW, destH, 0);
685
+ resetAlpha(dest, destW, destH);
686
+ } //
509
687
  // Copy data back to typed array
510
688
  //
511
689
  // 32-bit copy is much faster in chrome
512
690
 
513
- var dest32 = new Uint32Array(dest.buffer);
514
- dest32.set(new Uint32Array(this.__memory.buffer, 0, destH * destW)); // That's faster than doing checks in convolver.
515
- // !!! Note, canvas data is not premultipled. We don't need other
516
- // alpha corrections.
517
691
 
518
- if (!alpha) resetAlpha(dest, destW, destH);
692
+ var dest32 = new Uint32Array(dest.buffer);
693
+ dest32.set(new Uint32Array(this.__memory.buffer, 0, destH * destW));
519
694
  return dest;
520
695
  };
521
696
 
@@ -986,7 +1161,18 @@ module.exports.cib_support = function cib_support(createCanvas) {
986
1161
 
987
1162
  module.exports.worker_offscreen_canvas_support = function worker_offscreen_canvas_support() {
988
1163
  return new Promise(function (resolve, reject) {
1164
+ if (typeof OffscreenCanvas === 'undefined') {
1165
+ // if OffscreenCanvas is present, we assume browser supports Worker and built-in Promise as well
1166
+ resolve(false);
1167
+ return;
1168
+ }
1169
+
989
1170
  function workerPayload(self) {
1171
+ if (typeof createImageBitmap === 'undefined') {
1172
+ self.postMessage(false);
1173
+ return;
1174
+ }
1175
+
990
1176
  Promise.resolve().then(function () {
991
1177
  var canvas = new OffscreenCanvas(10, 10); // test that 2d context can be used in worker
992
1178
 
@@ -1005,10 +1191,75 @@ module.exports.worker_offscreen_canvas_support = function worker_offscreen_canva
1005
1191
  var w = new Worker("data:text/javascript;base64,".concat(code));
1006
1192
 
1007
1193
  w.onmessage = function (ev) {
1008
- return ev.data ? resolve() : reject();
1194
+ return resolve(ev.data);
1009
1195
  };
1010
1196
 
1011
1197
  w.onerror = reject;
1198
+ }).then(function (result) {
1199
+ return result;
1200
+ }, function () {
1201
+ return false;
1202
+ });
1203
+ }; // Check if canvas.getContext('2d').getImageData can be used,
1204
+ // FireFox randomizes the output of that function in `privacy.resistFingerprinting` mode
1205
+
1206
+
1207
+ module.exports.can_use_canvas = function can_use_canvas(createCanvas) {
1208
+ var usable = false;
1209
+
1210
+ try {
1211
+ var canvas = createCanvas(2, 1);
1212
+ var ctx = canvas.getContext('2d');
1213
+ var d = ctx.createImageData(2, 1);
1214
+ d.data[0] = 12;
1215
+ d.data[1] = 23;
1216
+ d.data[2] = 34;
1217
+ d.data[3] = 255;
1218
+ d.data[4] = 45;
1219
+ d.data[5] = 56;
1220
+ d.data[6] = 67;
1221
+ d.data[7] = 255;
1222
+ ctx.putImageData(d, 0, 0);
1223
+ d = null;
1224
+ d = ctx.getImageData(0, 0, 2, 1);
1225
+
1226
+ if (d.data[0] === 12 && d.data[1] === 23 && d.data[2] === 34 && d.data[3] === 255 && d.data[4] === 45 && d.data[5] === 56 && d.data[6] === 67 && d.data[7] === 255) {
1227
+ usable = true;
1228
+ }
1229
+ } catch (err) {}
1230
+
1231
+ return usable;
1232
+ }; // Check if createImageBitmap(img, sx, sy, sw, sh) signature works correctly
1233
+ // with JPEG images oriented with Exif;
1234
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1220671
1235
+ // TODO: remove after it's fixed in chrome for at least 2 releases
1236
+
1237
+
1238
+ module.exports.cib_can_use_region = function cib_can_use_region() {
1239
+ return new Promise(function (resolve) {
1240
+ if (typeof createImageBitmap === 'undefined') {
1241
+ resolve(false);
1242
+ return;
1243
+ }
1244
+
1245
+ var image = new Image();
1246
+ image.src = 'data:image/jpeg;base64,' + '/9j/4QBiRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAYAAAEaAAUAAAABAAAASgEbAAUAA' + 'AABAAAAUgEoAAMAAAABAAIAAAITAAMAAAABAAEAAAAAAAAAAABIAAAAAQAAAEgAAAAB/9' + 'sAQwAEAwMEAwMEBAMEBQQEBQYKBwYGBgYNCQoICg8NEBAPDQ8OERMYFBESFxIODxUcFRc' + 'ZGRsbGxAUHR8dGh8YGhsa/9sAQwEEBQUGBQYMBwcMGhEPERoaGhoaGhoaGhoaGhoaGhoa' + 'GhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoa/8IAEQgAAQACAwERAAIRAQMRA' + 'f/EABQAAQAAAAAAAAAAAAAAAAAAAAf/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAA' + 'IQAxAAAAF/P//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAA' + 'AAAAAAAAAAAAAAAD/2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIB' + 'AT8Bf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAA' + 'AAAAAAAAAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQH//EABQRAQAAAAAAAAAAAAAAAA' + 'AAAAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//EABQ' + 'QAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z';
1247
+
1248
+ image.onload = function () {
1249
+ createImageBitmap(image, 0, 0, image.width, image.height).then(function (bitmap) {
1250
+ if (bitmap.width === image.width && bitmap.height === image.height) {
1251
+ resolve(true);
1252
+ } else {
1253
+ resolve(false);
1254
+ }
1255
+ }, function () {
1256
+ return resolve(false);
1257
+ });
1258
+ };
1259
+
1260
+ image.onerror = function () {
1261
+ return resolve(false);
1262
+ };
1012
1263
  });
1013
1264
  };
1014
1265
 
@@ -1028,16 +1279,15 @@ module.exports = function () {
1028
1279
 
1029
1280
  if (!tileOpts.src && tileOpts.srcBitmap) {
1030
1281
  var canvas = new OffscreenCanvas(tileOpts.width, tileOpts.height);
1031
- var ctx = canvas.getContext('2d', {
1032
- alpha: Boolean(tileOpts.alpha)
1033
- });
1282
+ var ctx = canvas.getContext('2d');
1034
1283
  ctx.drawImage(tileOpts.srcBitmap, 0, 0);
1035
1284
  tileOpts.src = ctx.getImageData(0, 0, tileOpts.width, tileOpts.height).data;
1036
1285
  canvas.width = canvas.height = 0;
1037
1286
  canvas = null;
1038
1287
  tileOpts.srcBitmap.close();
1039
- tileOpts.srcBitmap = null;
1040
- returnBitmap = true;
1288
+ tileOpts.srcBitmap = null; // Temporary force out data to typed array, because Chrome have artefacts
1289
+ // https://github.com/nodeca/pica/issues/223
1290
+ // returnBitmap = true;
1041
1291
  }
1042
1292
 
1043
1293
  if (!mathLib) mathLib = new MathLib(ev.data.features); // Use multimath's sync auto-init. Avoid Promise use in old browsers,
@@ -1050,9 +1300,7 @@ module.exports = function () {
1050
1300
 
1051
1301
  var _canvas = new OffscreenCanvas(tileOpts.toWidth, tileOpts.toHeight);
1052
1302
 
1053
- var _ctx = _canvas.getContext('2d', {
1054
- alpha: Boolean(tileOpts.alpha)
1055
- });
1303
+ var _ctx = _canvas.getContext('2d');
1056
1304
 
1057
1305
  _ctx.putImageData(toImageData, 0, 0);
1058
1306
 
@@ -1191,35 +1439,6 @@ function blurMono16(src, width, height, radius) {
1191
1439
  module.exports = blurMono16;
1192
1440
 
1193
1441
  },{}],19:[function(_dereq_,module,exports){
1194
- if (typeof Object.create === 'function') {
1195
- // implementation from standard node.js 'util' module
1196
- module.exports = function inherits(ctor, superCtor) {
1197
- if (superCtor) {
1198
- ctor.super_ = superCtor
1199
- ctor.prototype = Object.create(superCtor.prototype, {
1200
- constructor: {
1201
- value: ctor,
1202
- enumerable: false,
1203
- writable: true,
1204
- configurable: true
1205
- }
1206
- })
1207
- }
1208
- };
1209
- } else {
1210
- // old school shim for old browsers
1211
- module.exports = function inherits(ctor, superCtor) {
1212
- if (superCtor) {
1213
- ctor.super_ = superCtor
1214
- var TempCtor = function () {}
1215
- TempCtor.prototype = superCtor.prototype
1216
- ctor.prototype = new TempCtor()
1217
- ctor.prototype.constructor = ctor
1218
- }
1219
- }
1220
- }
1221
-
1222
- },{}],20:[function(_dereq_,module,exports){
1223
1442
  'use strict';
1224
1443
 
1225
1444
 
@@ -1378,7 +1597,7 @@ MultiMath.prototype.__align = function align(number, base) {
1378
1597
 
1379
1598
  module.exports = MultiMath;
1380
1599
 
1381
- },{"./lib/base64decode":21,"./lib/wa_detect":22,"object-assign":23}],21:[function(_dereq_,module,exports){
1600
+ },{"./lib/base64decode":20,"./lib/wa_detect":21,"object-assign":22}],20:[function(_dereq_,module,exports){
1382
1601
  // base64 decode str -> Uint8Array, to load WA modules
1383
1602
  //
1384
1603
  'use strict';
@@ -1426,7 +1645,7 @@ module.exports = function base64decode(str) {
1426
1645
  return out;
1427
1646
  };
1428
1647
 
1429
- },{}],22:[function(_dereq_,module,exports){
1648
+ },{}],21:[function(_dereq_,module,exports){
1430
1649
  // Detect WebAssembly support.
1431
1650
  // - Check global WebAssembly object
1432
1651
  // - Try to load simple module (can be disabled via CSP)
@@ -1465,7 +1684,7 @@ module.exports = function hasWebAssembly() {
1465
1684
  return wa;
1466
1685
  };
1467
1686
 
1468
- },{}],23:[function(_dereq_,module,exports){
1687
+ },{}],22:[function(_dereq_,module,exports){
1469
1688
  /*
1470
1689
  object-assign
1471
1690
  (c) Sindre Sorhus
@@ -1557,7 +1776,7 @@ module.exports = shouldUseNative() ? Object.assign : function (target, source) {
1557
1776
  return to;
1558
1777
  };
1559
1778
 
1560
- },{}],24:[function(_dereq_,module,exports){
1779
+ },{}],23:[function(_dereq_,module,exports){
1561
1780
  var bundleFn = arguments[3];
1562
1781
  var sources = arguments[4];
1563
1782
  var cache = arguments[5];
@@ -1650,7 +1869,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
1650
1869
 
1651
1870
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
1652
1871
 
1653
- function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
1872
+ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
1654
1873
 
1655
1874
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
1656
1875
 
@@ -1668,7 +1887,9 @@ var worker = _dereq_('./lib/worker');
1668
1887
 
1669
1888
  var createStages = _dereq_('./lib/stepper');
1670
1889
 
1671
- var createRegions = _dereq_('./lib/tiler'); // Deduplicate pools & limiters with the same configs
1890
+ var createRegions = _dereq_('./lib/tiler');
1891
+
1892
+ var filter_info = _dereq_('./lib/mm_resize/resize_filter_info'); // Deduplicate pools & limiters with the same configs
1672
1893
  // when user creates multiple pica instances.
1673
1894
 
1674
1895
 
@@ -1700,15 +1921,16 @@ var DEFAULT_PICA_OPTS = {
1700
1921
  }
1701
1922
  };
1702
1923
  var DEFAULT_RESIZE_OPTS = {
1703
- quality: 3,
1704
- alpha: false,
1924
+ filter: 'mks2013',
1705
1925
  unsharpAmount: 0,
1706
1926
  unsharpRadius: 0.0,
1707
1927
  unsharpThreshold: 0
1708
1928
  };
1709
- var CAN_NEW_IMAGE_DATA;
1710
- var CAN_CREATE_IMAGE_BITMAP;
1711
- var CAN_USE_OFFSCREEN_CANVAS;
1929
+ var CAN_NEW_IMAGE_DATA = false;
1930
+ var CAN_CREATE_IMAGE_BITMAP = false;
1931
+ var CAN_USE_CANVAS_GET_IMAGE_DATA = false;
1932
+ var CAN_USE_OFFSCREEN_CANVAS = false;
1933
+ var CAN_USE_CIB_REGION_FOR_IMAGE = false;
1712
1934
 
1713
1935
  function workerFabric() {
1714
1936
  return {
@@ -1759,16 +1981,12 @@ Pica.prototype.init = function () {
1759
1981
 
1760
1982
  if (this.__initPromise) return this.__initPromise; // Test if we can create ImageData without canvas and memory copy
1761
1983
 
1762
- if (CAN_NEW_IMAGE_DATA !== false && CAN_NEW_IMAGE_DATA !== true) {
1763
- CAN_NEW_IMAGE_DATA = false;
1764
-
1765
- if (typeof ImageData !== 'undefined' && typeof Uint8ClampedArray !== 'undefined') {
1766
- try {
1767
- /* eslint-disable no-new */
1768
- new ImageData(new Uint8ClampedArray(400), 10, 10);
1769
- CAN_NEW_IMAGE_DATA = true;
1770
- } catch (__) {}
1771
- }
1984
+ if (typeof ImageData !== 'undefined' && typeof Uint8ClampedArray !== 'undefined') {
1985
+ try {
1986
+ /* eslint-disable no-new */
1987
+ new ImageData(new Uint8ClampedArray(400), 10, 10);
1988
+ CAN_NEW_IMAGE_DATA = true;
1989
+ } catch (__) {}
1772
1990
  } // ImageBitmap can be effective in 2 places:
1773
1991
  //
1774
1992
  // 1. Threaded jpeg unpack (basic)
@@ -1778,15 +1996,11 @@ Pica.prototype.init = function () {
1778
1996
  // see https://developer.mozilla.org/ru/docs/Web/API/ImageBitmap
1779
1997
 
1780
1998
 
1781
- if (CAN_CREATE_IMAGE_BITMAP !== false && CAN_CREATE_IMAGE_BITMAP !== true) {
1782
- CAN_CREATE_IMAGE_BITMAP = false;
1783
-
1784
- if (typeof ImageBitmap !== 'undefined') {
1785
- if (ImageBitmap.prototype && ImageBitmap.prototype.close) {
1786
- CAN_CREATE_IMAGE_BITMAP = true;
1787
- } else {
1788
- this.debug('ImageBitmap does not support .close(), disabled');
1789
- }
1999
+ if (typeof ImageBitmap !== 'undefined') {
2000
+ if (ImageBitmap.prototype && ImageBitmap.prototype.close) {
2001
+ CAN_CREATE_IMAGE_BITMAP = true;
2002
+ } else {
2003
+ this.debug('ImageBitmap does not support .close(), disabled');
1790
2004
  }
1791
2005
  }
1792
2006
 
@@ -1842,21 +2056,26 @@ Pica.prototype.init = function () {
1842
2056
  });
1843
2057
  }
1844
2058
 
2059
+ CAN_USE_CANVAS_GET_IMAGE_DATA = utils.can_use_canvas(this.options.createCanvas);
1845
2060
  var checkOffscreenCanvas;
1846
2061
 
1847
2062
  if (CAN_CREATE_IMAGE_BITMAP && CAN_NEW_IMAGE_DATA && features.indexOf('ww') !== -1) {
1848
2063
  checkOffscreenCanvas = utils.worker_offscreen_canvas_support();
1849
2064
  } else {
1850
- checkOffscreenCanvas = Promise.reject();
2065
+ checkOffscreenCanvas = Promise.resolve(false);
1851
2066
  }
1852
2067
 
1853
- checkOffscreenCanvas = checkOffscreenCanvas.then(function () {
1854
- CAN_USE_OFFSCREEN_CANVAS = true;
1855
- }, function () {
1856
- CAN_USE_OFFSCREEN_CANVAS = false;
2068
+ checkOffscreenCanvas = checkOffscreenCanvas.then(function (result) {
2069
+ CAN_USE_OFFSCREEN_CANVAS = result;
2070
+ }); // we use createImageBitmap to crop image data and pass it to workers,
2071
+ // so need to check whether function works correctly;
2072
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1220671
2073
+
2074
+ var checkCibRegion = utils.cib_can_use_region().then(function (result) {
2075
+ CAN_USE_CIB_REGION_FOR_IMAGE = result;
1857
2076
  }); // Init math lib. That's async because can load some
1858
2077
 
1859
- this.__initPromise = Promise.all([initMath, checkCibResize, checkOffscreenCanvas]).then(function () {
2078
+ this.__initPromise = Promise.all([initMath, checkCibResize, checkOffscreenCanvas, checkCibRegion]).then(function () {
1860
2079
  return _this;
1861
2080
  });
1862
2081
  return this.__initPromise;
@@ -1908,7 +2127,10 @@ Pica.prototype.__invokeResize = function (tileOpts, opts) {
1908
2127
 
1909
2128
 
1910
2129
  Pica.prototype.__extractTileData = function (tile, from, opts, stageEnv, extractTo) {
1911
- if (this.features.ww && CAN_USE_OFFSCREEN_CANVAS) {
2130
+ if (this.features.ww && CAN_USE_OFFSCREEN_CANVAS && ( // createImageBitmap doesn't work for images (Image, ImageBitmap) with Exif orientation in Chrome,
2131
+ // can use canvas because canvas doesn't have orientation;
2132
+ // see https://bugs.chromium.org/p/chromium/issues/detail?id=1220671
2133
+ utils.isCanvas(from) || CAN_USE_CIB_REGION_FOR_IMAGE)) {
1912
2134
  this.debug('Create tile for OffscreenCanvas');
1913
2135
  return createImageBitmap(stageEnv.srcImageBitmap || from, tile.x, tile.y, tile.width, tile.height).then(function (bitmap) {
1914
2136
  extractTo.srcBitmap = bitmap;
@@ -1918,9 +2140,7 @@ Pica.prototype.__extractTileData = function (tile, from, opts, stageEnv, extract
1918
2140
 
1919
2141
 
1920
2142
  if (utils.isCanvas(from)) {
1921
- if (!stageEnv.srcCtx) stageEnv.srcCtx = from.getContext('2d', {
1922
- alpha: Boolean(opts.alpha)
1923
- }); // If input is Canvas - extract region data directly
2143
+ if (!stageEnv.srcCtx) stageEnv.srcCtx = from.getContext('2d'); // If input is Canvas - extract region data directly
1924
2144
 
1925
2145
  this.debug('Get tile pixel data');
1926
2146
  extractTo.src = stageEnv.srcCtx.getImageData(tile.x, tile.y, tile.width, tile.height).data;
@@ -1934,9 +2154,7 @@ Pica.prototype.__extractTileData = function (tile, from, opts, stageEnv, extract
1934
2154
 
1935
2155
  this.debug('Draw tile imageBitmap/image to temporary canvas');
1936
2156
  var tmpCanvas = this.options.createCanvas(tile.width, tile.height);
1937
- var tmpCtx = tmpCanvas.getContext('2d', {
1938
- alpha: Boolean(opts.alpha)
1939
- });
2157
+ var tmpCtx = tmpCanvas.getContext('2d');
1940
2158
  tmpCtx.globalCompositeOperation = 'copy';
1941
2159
  tmpCtx.drawImage(stageEnv.srcImageBitmap || from, tile.x, tile.y, tile.width, tile.height, 0, 0, tile.width, tile.height);
1942
2160
  this.debug('Get tile pixel data');
@@ -2009,8 +2227,7 @@ Pica.prototype.__tileAndResize = function (from, to, opts) {
2009
2227
  scaleY: tile.scaleY,
2010
2228
  offsetX: tile.offsetX,
2011
2229
  offsetY: tile.offsetY,
2012
- quality: opts.quality,
2013
- alpha: opts.alpha,
2230
+ filter: opts.filter,
2014
2231
  unsharpAmount: opts.unsharpAmount,
2015
2232
  unsharpRadius: opts.unsharpRadius,
2016
2233
  unsharpThreshold: opts.unsharpThreshold
@@ -2035,9 +2252,7 @@ Pica.prototype.__tileAndResize = function (from, to, opts) {
2035
2252
 
2036
2253
 
2037
2254
  return Promise.resolve().then(function () {
2038
- stageEnv.toCtx = to.getContext('2d', {
2039
- alpha: Boolean(opts.alpha)
2040
- });
2255
+ stageEnv.toCtx = to.getContext('2d');
2041
2256
  if (utils.isCanvas(from)) return null;
2042
2257
 
2043
2258
  if (utils.isImageBitmap(from)) {
@@ -2117,14 +2332,20 @@ Pica.prototype.__processStages = function (stages, from, to, opts) {
2117
2332
  toWidth = _stages$shift2[0],
2118
2333
  toHeight = _stages$shift2[1];
2119
2334
 
2120
- var isLastStage = stages.length === 0;
2335
+ var isLastStage = stages.length === 0; // Optimization for legacy filters -
2336
+ // only use user-defined quality for the last stage,
2337
+ // use simpler (Hamming) filter for the first stages where
2338
+ // scale factor is large enough (more than 2-3)
2339
+ //
2340
+ // For advanced filters (mks2013 and custom) - skip optimization,
2341
+ // because need to apply sharpening every time
2342
+
2343
+ var filter;
2344
+ if (isLastStage || filter_info.q2f.indexOf(opts.filter) < 0) filter = opts.filter;else if (opts.filter === 'box') filter = 'box';else filter = 'hamming';
2121
2345
  opts = assign({}, opts, {
2122
2346
  toWidth: toWidth,
2123
2347
  toHeight: toHeight,
2124
- // only use user-defined quality for the last stage,
2125
- // use simpler (Hamming) filter for the first stages where
2126
- // scale factor is large enough (more than 2-3)
2127
- quality: isLastStage ? opts.quality : Math.min(1, opts.quality)
2348
+ filter: filter
2128
2349
  });
2129
2350
  var tmpCanvas;
2130
2351
 
@@ -2152,14 +2373,12 @@ Pica.prototype.__processStages = function (stages, from, to, opts) {
2152
2373
  Pica.prototype.__resizeViaCreateImageBitmap = function (from, to, opts) {
2153
2374
  var _this5 = this;
2154
2375
 
2155
- var toCtx = to.getContext('2d', {
2156
- alpha: Boolean(opts.alpha)
2157
- });
2376
+ var toCtx = to.getContext('2d');
2158
2377
  this.debug('Resize via createImageBitmap()');
2159
2378
  return createImageBitmap(from, {
2160
2379
  resizeWidth: opts.toWidth,
2161
2380
  resizeHeight: opts.toHeight,
2162
- resizeQuality: utils.cib_quality_name(opts.quality)
2381
+ resizeQuality: utils.cib_quality_name(filter_info.f2q[opts.filter])
2163
2382
  }).then(function (imageBitmap) {
2164
2383
  if (opts.canceled) return opts.cancelToken; // if no unsharp - draw directly to output canvas
2165
2384
 
@@ -2177,9 +2396,7 @@ Pica.prototype.__resizeViaCreateImageBitmap = function (from, to, opts) {
2177
2396
 
2178
2397
  var tmpCanvas = _this5.options.createCanvas(opts.toWidth, opts.toHeight);
2179
2398
 
2180
- var tmpCtx = tmpCanvas.getContext('2d', {
2181
- alpha: Boolean(opts.alpha)
2182
- });
2399
+ var tmpCtx = tmpCanvas.getContext('2d');
2183
2400
  tmpCtx.drawImage(imageBitmap, 0, 0);
2184
2401
  imageBitmap.close();
2185
2402
  var iData = tmpCtx.getImageData(0, 0, opts.toWidth, opts.toHeight);
@@ -2215,7 +2432,16 @@ Pica.prototype.resize = function (from, to, options) {
2215
2432
  opts.toWidth = to.width;
2216
2433
  opts.toHeight = to.height;
2217
2434
  opts.width = from.naturalWidth || from.width;
2218
- opts.height = from.naturalHeight || from.height; // Prevent stepper from infinite loop
2435
+ opts.height = from.naturalHeight || from.height; // Legacy `.quality` option
2436
+
2437
+ if (Object.prototype.hasOwnProperty.call(opts, 'quality')) {
2438
+ if (opts.quality < 0 || opts.quality > 3) {
2439
+ throw new Error("Pica: .quality should be [0..3], got ".concat(opts.quality));
2440
+ }
2441
+
2442
+ opts.filter = filter_info.q2f[opts.quality];
2443
+ } // Prevent stepper from infinite loop
2444
+
2219
2445
 
2220
2446
  if (to.width === 0 || to.height === 0) {
2221
2447
  return Promise.reject(new Error("Invalid output size: ".concat(to.width, "x").concat(to.height)));
@@ -2242,7 +2468,17 @@ Pica.prototype.resize = function (from, to, options) {
2242
2468
  if (opts.canceled) return opts.cancelToken; // if createImageBitmap supports resize, just do it and return
2243
2469
 
2244
2470
  if (_this6.features.cib) {
2245
- return _this6.__resizeViaCreateImageBitmap(from, to, opts);
2471
+ if (filter_info.q2f.indexOf(opts.filter) >= 0) {
2472
+ return _this6.__resizeViaCreateImageBitmap(from, to, opts);
2473
+ }
2474
+
2475
+ _this6.debug('cib is enabled, but not supports provided filter, fallback to manual math');
2476
+ }
2477
+
2478
+ if (!CAN_USE_CANVAS_GET_IMAGE_DATA) {
2479
+ var err = new Error('Pica: cannot use getImageData on canvas, ' + "make sure fingerprinting protection isn't enabled");
2480
+ err.code = 'ERR_GET_IMAGE_DATA';
2481
+ throw err;
2246
2482
  } //
2247
2483
  // No easy way, let's resize manually via arrays
2248
2484
  //
@@ -2258,7 +2494,16 @@ Pica.prototype.resize = function (from, to, options) {
2258
2494
  Pica.prototype.resizeBuffer = function (options) {
2259
2495
  var _this7 = this;
2260
2496
 
2261
- var opts = assign({}, DEFAULT_RESIZE_OPTS, options);
2497
+ var opts = assign({}, DEFAULT_RESIZE_OPTS, options); // Legacy `.quality` option
2498
+
2499
+ if (Object.prototype.hasOwnProperty.call(opts, 'quality')) {
2500
+ if (opts.quality < 0 || opts.quality > 3) {
2501
+ throw new Error("Pica: .quality should be [0..3], got ".concat(opts.quality));
2502
+ }
2503
+
2504
+ opts.filter = filter_info.q2f[opts.quality];
2505
+ }
2506
+
2262
2507
  return this.init().then(function () {
2263
2508
  return _this7.__mathlib.resizeAndUnsharp(opts);
2264
2509
  });
@@ -2301,5 +2546,5 @@ Pica.prototype.debug = function () {};
2301
2546
 
2302
2547
  module.exports = Pica;
2303
2548
 
2304
- },{"./lib/mathlib":1,"./lib/pool":13,"./lib/stepper":14,"./lib/tiler":15,"./lib/utils":16,"./lib/worker":17,"object-assign":23,"webworkify":24}]},{},[])("/index.js")
2549
+ },{"./lib/mathlib":1,"./lib/mm_resize/resize_filter_info":7,"./lib/pool":13,"./lib/stepper":14,"./lib/tiler":15,"./lib/utils":16,"./lib/worker":17,"object-assign":22,"webworkify":23}]},{},[])("/index.js")
2305
2550
  });