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.
@@ -4,6 +4,10 @@ inline uint8_t clampTo8(int32_t i) {
4
4
  return i < 0 ? 0 : (i > 255 ? 255 : i);
5
5
  }
6
6
 
7
+ inline uint32_t clampNegative(int32_t i) {
8
+ return i >= 0 ? i : 0;
9
+ }
10
+
7
11
 
8
12
  #define R(x) ((uint8_t)(x))
9
13
  #define G(x) ((uint8_t)((x) >> 8))
@@ -13,10 +17,9 @@ inline uint8_t clampTo8(int32_t i) {
13
17
  #define RGBA(r, g, b, a) ((r) | ((g) << 8) | ((b) << 16) | ((a) << 24))
14
18
 
15
19
 
16
- void convolve(uint32_t *src, uint32_t *dest, uint32_t srcW, uint32_t srcH, uint32_t destW, int16_t *filters)
20
+ void convolveHor(uint32_t *src, uint16_t *dest, uint32_t srcW, uint32_t srcH, uint32_t destW, int16_t *filters)
17
21
  {
18
22
  int32_t r, g, b, a;
19
- uint32_t rgba;
20
23
  uint32_t filterPtr, filterShift, filterSize;
21
24
  uint32_t srcPtr, srcY, destX;
22
25
  int32_t filterVal;
@@ -28,53 +31,226 @@ void convolve(uint32_t *src, uint32_t *dest, uint32_t srcW, uint32_t srcH, uint3
28
31
  for (destX = 0; destX < destW; destX++) {
29
32
  // Get the filter that determines the current output pixel.
30
33
  filterShift = filters[filterPtr++];
31
- srcPtr = srcOffset + filterShift;
32
34
  filterSize = filters[filterPtr++];
33
35
 
36
+ srcPtr = srcOffset + filterShift;
37
+
34
38
  r = g = b = a = 0;
35
39
  // Apply the filter to the row to get the destination pixel r, g, b, a
36
40
  for (; filterSize > 0; filterSize--) {
37
41
  filterVal = filters[filterPtr++];
38
- rgba = src[srcPtr++];
42
+ uint32_t rgba = src[srcPtr++];
39
43
 
40
44
  r += filterVal * R(rgba);
41
45
  g += filterVal * G(rgba);
42
46
  b += filterVal * B(rgba);
43
47
  a += filterVal * A(rgba);
44
48
  };
45
- // Bring this value back in range. All of the filter scaling factors
46
- // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
49
+
50
+ // Store 15 bits between passes for better precision
51
+ // Instead of shift to 14 (FIXED_FRAC_BITS), shift to 7 only
52
+ //
53
+ dest[destOffset++] = clampNegative(r >> 7);
54
+ dest[destOffset++] = clampNegative(g >> 7);
55
+ dest[destOffset++] = clampNegative(b >> 7);
56
+ dest[destOffset++] = clampNegative(a >> 7);
57
+ destOffset += srcH * 4 - 4;
58
+ };
59
+ destOffset = (srcY + 1) * 4;
60
+ srcOffset = (srcY + 1) * srcW;
61
+ };
62
+ }
63
+
64
+
65
+ void convolveVert(uint16_t *src, uint32_t *dest, uint32_t srcW, uint32_t srcH, uint32_t destW, int16_t *filters)
66
+ {
67
+ int32_t r, g, b, a;
68
+ uint32_t filterPtr, filterShift, filterSize;
69
+ uint32_t srcPtr, srcY, destX;
70
+ int32_t filterVal;
71
+ uint32_t srcOffset = 0, destOffset = 0;
72
+ //
73
+ for (srcY=0; srcY < srcH; srcY++) {
74
+ filterPtr = 0;
75
+
76
+ // Apply precomputed filters to each destination row point
77
+ for (destX = 0; destX < destW; destX++) {
78
+ // Get the filter that determines the current output pixel.
79
+ filterShift = filters[filterPtr++];
80
+ filterSize = filters[filterPtr++];
81
+
82
+ srcPtr = srcOffset + filterShift*4;
83
+
84
+ r = g = b = a = 0;
85
+
86
+ // Apply the filter to the row to get the destination pixel r, g, b, a
87
+ for (; filterSize > 0; filterSize--) {
88
+ filterVal = filters[filterPtr++];
89
+
90
+ r += filterVal * src[srcPtr++];
91
+ g += filterVal * src[srcPtr++];
92
+ b += filterVal * src[srcPtr++];
93
+ a += filterVal * src[srcPtr++];
94
+ };
95
+
96
+ // Sync with premultiplied version for exact result match
97
+ r >>= 7;
98
+ g >>= 7;
99
+ b >>= 7;
100
+ a >>= 7;
101
+
102
+ // Bring this value back in range + round result.
47
103
  //
48
- // (!) Add 1/2 of value before clamping to get proper rounding. In other
49
- // case brightness loss will be noticeable if you resize image with white
50
- // border and place it on white background.
51
104
  dest[destOffset] = RGBA(
52
- clampTo8((r + (1<<13))>>14 /*FIXED_FRAC_BITS*/),
53
- clampTo8((g + (1<<13))>>14 /*FIXED_FRAC_BITS*/),
54
- clampTo8((b + (1<<13))>>14 /*FIXED_FRAC_BITS*/),
55
- clampTo8((a + (1<<13))>>14 /*FIXED_FRAC_BITS*/));
105
+ clampTo8((r + (1 << 13)) >> 14),
106
+ clampTo8((g + (1 << 13)) >> 14),
107
+ clampTo8((b + (1 << 13)) >> 14),
108
+ clampTo8((a + (1 << 13)) >> 14)
109
+ );
56
110
  destOffset += srcH;
57
111
  };
58
112
  destOffset = srcY + 1;
113
+ srcOffset = (srcY + 1) * srcW * 4;
114
+ };
115
+ }
116
+
117
+
118
+ void convolveHorWithPre(uint32_t *src, uint16_t *dest, uint32_t srcW, uint32_t srcH, uint32_t destW, int16_t *filters)
119
+ {
120
+ int32_t r, g, b, a;
121
+ uint32_t filterPtr, filterShift, filterSize;
122
+ uint32_t srcPtr, srcY, destX;
123
+ int32_t filterVal;
124
+ uint32_t srcOffset = 0, destOffset = 0;
125
+ //
126
+ for (srcY=0; srcY < srcH; srcY++) {
127
+ filterPtr = 0;
128
+ // Apply precomputed filters to each destination row point
129
+ for (destX = 0; destX < destW; destX++) {
130
+ // Get the filter that determines the current output pixel.
131
+ filterShift = filters[filterPtr++];
132
+ filterSize = filters[filterPtr++];
133
+
134
+ srcPtr = srcOffset + filterShift;
135
+
136
+ r = g = b = a = 0;
137
+ // Apply the filter to the row to get the destination pixel r, g, b, a
138
+ for (; filterSize > 0; filterSize--) {
139
+ filterVal = filters[filterPtr++];
140
+ uint32_t rgba = src[srcPtr++];
141
+
142
+ uint8_t alpha = A(rgba);
143
+
144
+ r += filterVal * alpha * R(rgba);
145
+ g += filterVal * alpha * G(rgba);
146
+ b += filterVal * alpha * B(rgba);
147
+ a += filterVal * alpha;
148
+ };
149
+
150
+ // Premultiply is (* alpha / 255).
151
+ // Postpone division for better performance
152
+ r /= 255;
153
+ g /= 255;
154
+ b /= 255;
155
+
156
+ // Store 15 bits between passes for better precision
157
+ // Instead of shift to 14 (FIXED_FRAC_BITS), shift to 7 only
158
+ //
159
+ dest[destOffset++] = clampNegative(r >> 7);
160
+ dest[destOffset++] = clampNegative(g >> 7);
161
+ dest[destOffset++] = clampNegative(b >> 7);
162
+ dest[destOffset++] = clampNegative(a >> 7);
163
+ destOffset += srcH * 4 - 4;
164
+ };
165
+ destOffset = (srcY + 1) * 4;
59
166
  srcOffset = (srcY + 1) * srcW;
60
167
  };
61
168
  }
62
169
 
170
+
171
+ void convolveVertWithPre(uint16_t *src, uint32_t *dest, uint32_t srcW, uint32_t srcH, uint32_t destW, int16_t *filters)
172
+ {
173
+ int32_t r, g, b, a;
174
+ uint32_t filterPtr, filterShift, filterSize;
175
+ uint32_t srcPtr, srcY, destX;
176
+ int32_t filterVal;
177
+ uint32_t srcOffset = 0, destOffset = 0;
178
+ //
179
+ for (srcY=0; srcY < srcH; srcY++) {
180
+ filterPtr = 0;
181
+
182
+ // Apply precomputed filters to each destination row point
183
+ for (destX = 0; destX < destW; destX++) {
184
+ // Get the filter that determines the current output pixel.
185
+ filterShift = filters[filterPtr++];
186
+ filterSize = filters[filterPtr++];
187
+
188
+ srcPtr = srcOffset + filterShift*4;
189
+
190
+ r = g = b = a = 0;
191
+
192
+ // Apply the filter to the row to get the destination pixel r, g, b, a
193
+ for (; filterSize > 0; filterSize--) {
194
+ filterVal = filters[filterPtr++];
195
+
196
+ r += filterVal * src[srcPtr++];
197
+ g += filterVal * src[srcPtr++];
198
+ b += filterVal * src[srcPtr++];
199
+ a += filterVal * src[srcPtr++];
200
+ };
201
+
202
+ // Downscale to leave room for un-premultiply
203
+ r >>= 7;
204
+ g >>= 7;
205
+ b >>= 7;
206
+ a >>= 7;
207
+
208
+ // Un-premultiply
209
+ a = clampTo8((a + (1 << 13)) >> 14);
210
+ if (a > 0) {
211
+ r = r * 255 / a;
212
+ g = g * 255 / a;
213
+ b = b * 255 / a;
214
+ }
215
+
216
+ // Bring this value back in range + round result.
217
+ // Shift value = FIXED_FRAC_BITS + 7
218
+ //
219
+ dest[destOffset] = RGBA(
220
+ clampTo8((r + (1 << 13)) >> 14),
221
+ clampTo8((g + (1 << 13)) >> 14),
222
+ clampTo8((b + (1 << 13)) >> 14),
223
+ a
224
+ );
225
+ destOffset += srcH;
226
+ };
227
+ destOffset = srcY + 1;
228
+ srcOffset = (srcY + 1) * srcW * 4;
229
+ };
230
+ }
231
+
232
+
63
233
  void convolveHV(uint32_t filtersX_offset,
64
234
  uint32_t filtersY_offset,
65
235
  uint32_t tmp_offset,
66
236
  uint32_t srcW,
67
237
  uint32_t srcH,
68
238
  uint32_t destW,
69
- uint32_t destH)
239
+ uint32_t destH,
240
+ uint8_t has_alpha)
70
241
  {
71
242
  uint8_t *memory = 0;
72
243
 
73
244
  uint32_t *src = (uint32_t *)memory;
245
+ uint16_t *tmp = (uint16_t *)(memory + tmp_offset);
74
246
  int16_t *filterX = (int16_t *)(memory + filtersX_offset);
75
247
  int16_t *filterY = (int16_t *)(memory + filtersY_offset);
76
- uint32_t *tmp = (uint32_t *)(memory + tmp_offset);
77
248
 
78
- convolve(src, tmp, srcW, srcH, destW, filterX);
79
- convolve(tmp, src, srcH, destW, destH, filterY);
249
+ if (has_alpha) {
250
+ convolveHorWithPre(src, tmp, srcW, srcH, destW, filterX);
251
+ convolveVertWithPre(tmp, src, srcH, destW, destH, filterY);
252
+ } else {
253
+ convolveHor(src, tmp, srcW, srcH, destW, filterX);
254
+ convolveVert(tmp, src, srcH, destW, destH, filterY);
255
+ }
80
256
  }
@@ -8,18 +8,19 @@
8
8
 
9
9
 
10
10
  function clampTo8(i) { return i < 0 ? 0 : (i > 255 ? 255 : i); }
11
+ function clampNegative(i) { return i >= 0 ? i : 0; }
11
12
 
12
-
13
- // Convolve image in horizontal directions and transpose output. In theory,
14
- // transpose allow:
13
+ // Convolve image data in horizontal direction. Can be used for:
14
+ //
15
+ // 1. bitmap with premultiplied alpha
16
+ // 2. bitmap without alpha (all values 255)
15
17
  //
16
- // - use the same convolver for both passes (this fails due different
17
- // types of input array and temporary buffer)
18
- // - making vertical pass by horisonltal lines inprove CPU cache use.
18
+ // Notes:
19
19
  //
20
- // But in real life this doesn't work :)
20
+ // - output is transposed
21
+ // - output resolution is ~15 bits per channel(for better precision).
21
22
  //
22
- function convolveHorizontally(src, dest, srcW, srcH, destW, filters) {
23
+ function convolveHor(src, dest, srcW, srcH, destW, filters) {
23
24
 
24
25
  var r, g, b, a;
25
26
  var filterPtr, filterShift, filterSize;
@@ -53,17 +54,69 @@ function convolveHorizontally(src, dest, srcW, srcH, destW, filters) {
53
54
  srcPtr = (srcPtr + 4)|0;
54
55
  }
55
56
 
56
- // Bring this value back in range. All of the filter scaling factors
57
- // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
57
+ // Store 15 bits between passes for better precision
58
+ // Instead of shift to 14 (FIXED_FRAC_BITS), shift to 7 only
58
59
  //
59
- // (!) Add 1/2 of value before clamping to get proper rounding. In other
60
- // case brightness loss will be noticeable if you resize image with white
61
- // border and place it on white background.
60
+ dest[destOffset + 3] = clampNegative(a >> 7);
61
+ dest[destOffset + 2] = clampNegative(b >> 7);
62
+ dest[destOffset + 1] = clampNegative(g >> 7);
63
+ dest[destOffset] = clampNegative(r >> 7);
64
+ destOffset = (destOffset + srcH * 4)|0;
65
+ }
66
+
67
+ destOffset = ((srcY + 1) * 4)|0;
68
+ srcOffset = ((srcY + 1) * srcW * 4)|0;
69
+ }
70
+ }
71
+
72
+ // Supplementary method for `convolveHor()`
73
+ //
74
+ function convolveVert(src, dest, srcW, srcH, destW, filters) {
75
+
76
+ var r, g, b, a;
77
+ var filterPtr, filterShift, filterSize;
78
+ var srcPtr, srcY, destX, filterVal;
79
+ var srcOffset = 0, destOffset = 0;
80
+
81
+ // For each row
82
+ for (srcY = 0; srcY < srcH; srcY++) {
83
+ filterPtr = 0;
84
+
85
+ // Apply precomputed filters to each destination row point
86
+ for (destX = 0; destX < destW; destX++) {
87
+ // Get the filter that determines the current output pixel.
88
+ filterShift = filters[filterPtr++];
89
+ filterSize = filters[filterPtr++];
90
+
91
+ srcPtr = (srcOffset + (filterShift * 4))|0;
92
+
93
+ r = g = b = a = 0;
94
+
95
+ // Apply the filter to the row to get the destination pixel r, g, b, a
96
+ for (; filterSize > 0; filterSize--) {
97
+ filterVal = filters[filterPtr++];
98
+
99
+ // Use reverse order to workaround deopts in old v8 (node v.10)
100
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
101
+ a = (a + filterVal * src[srcPtr + 3])|0;
102
+ b = (b + filterVal * src[srcPtr + 2])|0;
103
+ g = (g + filterVal * src[srcPtr + 1])|0;
104
+ r = (r + filterVal * src[srcPtr])|0;
105
+ srcPtr = (srcPtr + 4)|0;
106
+ }
107
+
108
+ // Sync with premultiplied version for exact result match
109
+ r >>= 7;
110
+ g >>= 7;
111
+ b >>= 7;
112
+ a >>= 7;
113
+
114
+ // Bring this value back in range + round result.
62
115
  //
63
- dest[destOffset + 3] = clampTo8((a + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
64
- dest[destOffset + 2] = clampTo8((b + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
65
- dest[destOffset + 1] = clampTo8((g + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
66
- dest[destOffset] = clampTo8((r + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
116
+ dest[destOffset + 3] = clampTo8((a + (1 << 13)) >> 14);
117
+ dest[destOffset + 2] = clampTo8((b + (1 << 13)) >> 14);
118
+ dest[destOffset + 1] = clampTo8((g + (1 << 13)) >> 14);
119
+ dest[destOffset] = clampTo8((r + (1 << 13)) >> 14);
67
120
  destOffset = (destOffset + srcH * 4)|0;
68
121
  }
69
122
 
@@ -72,11 +125,73 @@ function convolveHorizontally(src, dest, srcW, srcH, destW, filters) {
72
125
  }
73
126
  }
74
127
 
75
- // Technically, convolvers are the same. But input array and temporary
76
- // buffer can be of different type (especially, in old browsers). So,
77
- // keep code in separate functions to avoid deoptimizations & speed loss.
78
128
 
79
- function convolveVertically(src, dest, srcW, srcH, destW, filters) {
129
+ // Premultiply & convolve image data in horizontal direction. Can be used for:
130
+ //
131
+ // - Any bitmap data, extracted with `.getImageData()` method (with
132
+ // non-premultiplied alpha)
133
+ //
134
+ // For images without alpha channel this method is slower than `convolveHor()`
135
+ //
136
+ function convolveHorWithPre(src, dest, srcW, srcH, destW, filters) {
137
+
138
+ var r, g, b, a, alpha;
139
+ var filterPtr, filterShift, filterSize;
140
+ var srcPtr, srcY, destX, filterVal;
141
+ var srcOffset = 0, destOffset = 0;
142
+
143
+ // For each row
144
+ for (srcY = 0; srcY < srcH; srcY++) {
145
+ filterPtr = 0;
146
+
147
+ // Apply precomputed filters to each destination row point
148
+ for (destX = 0; destX < destW; destX++) {
149
+ // Get the filter that determines the current output pixel.
150
+ filterShift = filters[filterPtr++];
151
+ filterSize = filters[filterPtr++];
152
+
153
+ srcPtr = (srcOffset + (filterShift * 4))|0;
154
+
155
+ r = g = b = a = 0;
156
+
157
+ // Apply the filter to the row to get the destination pixel r, g, b, a
158
+ for (; filterSize > 0; filterSize--) {
159
+ filterVal = filters[filterPtr++];
160
+
161
+ // Use reverse order to workaround deopts in old v8 (node v.10)
162
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
163
+ alpha = src[srcPtr + 3];
164
+ a = (a + filterVal * alpha)|0;
165
+ b = (b + filterVal * src[srcPtr + 2] * alpha)|0;
166
+ g = (g + filterVal * src[srcPtr + 1] * alpha)|0;
167
+ r = (r + filterVal * src[srcPtr] * alpha)|0;
168
+ srcPtr = (srcPtr + 4)|0;
169
+ }
170
+
171
+ // Premultiply is (* alpha / 255).
172
+ // Postpone division for better performance
173
+ b = (b / 255)|0;
174
+ g = (g / 255)|0;
175
+ r = (r / 255)|0;
176
+
177
+ // Store 15 bits between passes for better precision
178
+ // Instead of shift to 14 (FIXED_FRAC_BITS), shift to 7 only
179
+ //
180
+ dest[destOffset + 3] = clampNegative(a >> 7);
181
+ dest[destOffset + 2] = clampNegative(b >> 7);
182
+ dest[destOffset + 1] = clampNegative(g >> 7);
183
+ dest[destOffset] = clampNegative(r >> 7);
184
+ destOffset = (destOffset + srcH * 4)|0;
185
+ }
186
+
187
+ destOffset = ((srcY + 1) * 4)|0;
188
+ srcOffset = ((srcY + 1) * srcW * 4)|0;
189
+ }
190
+ }
191
+
192
+ // Supplementary method for `convolveHorWithPre()`
193
+ //
194
+ function convolveVertWithPre(src, dest, srcW, srcH, destW, filters) {
80
195
 
81
196
  var r, g, b, a;
82
197
  var filterPtr, filterShift, filterSize;
@@ -110,17 +225,27 @@ function convolveVertically(src, dest, srcW, srcH, destW, filters) {
110
225
  srcPtr = (srcPtr + 4)|0;
111
226
  }
112
227
 
113
- // Bring this value back in range. All of the filter scaling factors
114
- // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
115
- //
116
- // (!) Add 1/2 of value before clamping to get proper rounding. In other
117
- // case brightness loss will be noticeable if you resize image with white
118
- // border and place it on white background.
228
+ // Downscale to leave room for un-premultiply
229
+ r >>= 7;
230
+ g >>= 7;
231
+ b >>= 7;
232
+ a >>= 7;
233
+
234
+ // Un-premultiply
235
+ a = clampTo8((a + (1 << 13)) >> 14);
236
+ if (a > 0) {
237
+ r = (r * 255 / a)|0;
238
+ g = (g * 255 / a)|0;
239
+ b = (b * 255 / a)|0;
240
+ }
241
+
242
+ // Bring this value back in range + round result.
243
+ // Shift value = FIXED_FRAC_BITS + 7
119
244
  //
120
- dest[destOffset + 3] = clampTo8((a + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
121
- dest[destOffset + 2] = clampTo8((b + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
122
- dest[destOffset + 1] = clampTo8((g + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
123
- dest[destOffset] = clampTo8((r + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/);
245
+ dest[destOffset + 3] = a;
246
+ dest[destOffset + 2] = clampTo8((b + (1 << 13)) >> 14);
247
+ dest[destOffset + 1] = clampTo8((g + (1 << 13)) >> 14);
248
+ dest[destOffset] = clampTo8((r + (1 << 13)) >> 14);
124
249
  destOffset = (destOffset + srcH * 4)|0;
125
250
  }
126
251
 
@@ -131,6 +256,8 @@ function convolveVertically(src, dest, srcW, srcH, destW, filters) {
131
256
 
132
257
 
133
258
  module.exports = {
134
- convolveHorizontally,
135
- convolveVertically
259
+ convolveHor,
260
+ convolveVert,
261
+ convolveHorWithPre,
262
+ convolveVertWithPre
136
263
  };
Binary file
@@ -2,4 +2,4 @@
2
2
  //
3
3
  'use strict';
4
4
  /* eslint-disable max-len */
5
- module.exports = 'AGFzbQEAAAAADAZkeWxpbmsAAAAAAAEXA2AAAGAGf39/f39/AGAHf39/f39/fwACDwEDZW52Bm1lbW9yeQIAAAMEAwABAgYGAX8AQQALB1cFEV9fd2FzbV9jYWxsX2N0b3JzAAAIY29udm9sdmUAAQpjb252b2x2ZUhWAAIMX19kc29faGFuZGxlAwAYX193YXNtX2FwcGx5X2RhdGFfcmVsb2NzAAAK7AMDAwABC8YDAQ9/AkAgA0UNACAERQ0AA0AgDCENQQAhE0EAIQcDQCAHQQJqIQYCfyAHQQF0IAVqIgcuAQIiFEUEQEGAwAAhCEGAwAAhCUGAwAAhCkGAwAAhCyAGDAELIBIgBy4BAGohCEEAIQsgFCEHQQAhDiAGIQlBACEPQQAhEANAIAUgCUEBdGouAQAiESAAIAhBAnRqKAIAIgpBGHZsIBBqIRAgCkH/AXEgEWwgC2ohCyAKQRB2Qf8BcSARbCAPaiEPIApBCHZB/wFxIBFsIA5qIQ4gCEEBaiEIIAlBAWohCSAHQQFrIgcNAAsgC0GAQGshCCAOQYBAayEJIA9BgEBrIQogEEGAQGshCyAGIBRqCyEHIAEgDUECdGogCUEOdSIGQf8BIAZB/wFIGyIGQQAgBkEAShtBCHRBgP4DcSAKQQ51IgZB/wEgBkH/AUgbIgZBACAGQQBKG0EQdEGAgPwHcSALQQ51IgZB/wEgBkH/AUgbIgZBACAGQQBKG0EYdHJyIAhBDnUiBkH/ASAGQf8BSBsiBkEAIAZBAEobcjYCACADIA1qIQ0gE0EBaiITIARHDQALIAxBAWoiDCACbCESIAMgDEcNAAsLCx4AQQAgAiADIAQgBSAAEAEgAkEAIAQgBSAGIAEQAQs=';
5
+ module.exports = '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';
@@ -2,10 +2,18 @@
2
2
 
3
3
 
4
4
  const createFilters = require('./resize_filter_gen');
5
- const convolveHorizontally = require('./convolve').convolveHorizontally;
6
- const convolveVertically = require('./convolve').convolveVertically;
5
+ const { convolveHor, convolveVert, convolveHorWithPre, convolveVertWithPre } = require('./convolve');
7
6
 
8
7
 
8
+ function hasAlpha(src, width, height) {
9
+ let ptr = 3, len = (width * height * 4)|0;
10
+ while (ptr < len) {
11
+ if (src[ptr] !== 255) return true;
12
+ ptr = (ptr + 4)|0;
13
+ }
14
+ return false;
15
+ }
16
+
9
17
  function resetAlpha(dst, width, height) {
10
18
  let ptr = 3, len = (width * height * 4)|0;
11
19
  while (ptr < len) { dst[ptr] = 0xFF; ptr = (ptr + 4)|0; }
@@ -23,26 +31,22 @@ module.exports = function resize(options) {
23
31
  const offsetX = options.offsetX || 0;
24
32
  const offsetY = options.offsetY || 0;
25
33
  const dest = options.dest || new Uint8Array(destW * destH * 4);
26
- const quality = typeof options.quality === 'undefined' ? 3 : options.quality;
27
- const alpha = options.alpha || false;
28
-
29
- const filtersX = createFilters(quality, srcW, destW, scaleX, offsetX),
30
- filtersY = createFilters(quality, srcH, destH, scaleY, offsetY);
31
-
32
- const tmp = new Uint8Array(destW * srcH * 4);
33
-
34
- // To use single function we need src & tmp of the same type.
35
- // But src can be CanvasPixelArray, and tmp - Uint8Array. So, keep
36
- // vertical and horizontal passes separately to avoid deoptimization.
37
-
38
- convolveHorizontally(src, tmp, srcW, srcH, destW, filtersX);
39
- convolveVertically(tmp, dest, srcH, destW, destH, filtersY);
40
-
41
- // That's faster than doing checks in convolver.
42
- // !!! Note, canvas data is not premultipled. We don't need other
43
- // alpha corrections.
44
34
 
45
- if (!alpha) resetAlpha(dest, destW, destH);
35
+ const filter = typeof options.filter === 'undefined' ? 'mks2013' : options.filter;
36
+ const filtersX = createFilters(filter, srcW, destW, scaleX, offsetX),
37
+ filtersY = createFilters(filter, srcH, destH, scaleY, offsetY);
38
+
39
+ const tmp = new Uint16Array(destW * srcH * 4);
40
+
41
+ // Autodetect if alpha channel exists, and use appropriate method
42
+ if (hasAlpha(src, srcW, srcH)) {
43
+ convolveHorWithPre(src, tmp, srcW, srcH, destW, filtersX);
44
+ convolveVertWithPre(tmp, dest, srcH, destW, destH, filtersY);
45
+ } else {
46
+ convolveHor(src, tmp, srcW, srcH, destW, filtersX);
47
+ convolveVert(tmp, dest, srcH, destW, destH, filtersY);
48
+ resetAlpha(dest, destW, destH);
49
+ }
46
50
 
47
51
  return dest;
48
52
  };
@@ -21,15 +21,15 @@ function toFixedPoint(num) {
21
21
  }
22
22
 
23
23
 
24
- module.exports = function resizeFilterGen(quality, srcSize, destSize, scale, offset) {
24
+ module.exports = function resizeFilterGen(filter, srcSize, destSize, scale, offset) {
25
25
 
26
- var filterFunction = FILTER_INFO[quality].filter;
26
+ var filterFunction = FILTER_INFO.filter[filter].fn;
27
27
 
28
28
  var scaleInverted = 1.0 / scale;
29
29
  var scaleClamped = Math.min(1.0, scale); // For upscale
30
30
 
31
31
  // Filter window (averaging interval), scaled to src image
32
- var srcWindow = FILTER_INFO[quality].win / scaleClamped;
32
+ var srcWindow = FILTER_INFO.filter[filter].win / scaleClamped;
33
33
 
34
34
  var destPixel, srcPixel, srcFirst, srcLast, filterElementSize,
35
35
  floatFilter, fxpFilter, total, pxl, idx, floatVal, filterTotal, filterVal;
@@ -6,38 +6,65 @@
6
6
  'use strict';
7
7
 
8
8
 
9
- module.exports = [
10
- { // Nearest neibor (Box)
9
+ const filter = {
10
+ // Nearest neibor
11
+ box: {
11
12
  win: 0.5,
12
- filter: function (x) {
13
- return (x >= -0.5 && x < 0.5) ? 1.0 : 0.0;
13
+ fn: function (x) {
14
+ if (x < 0) x = -x;
15
+ return (x < 0.5) ? 1.0 : 0.0;
14
16
  }
15
17
  },
16
- { // Hamming
18
+ // // Hamming
19
+ hamming: {
17
20
  win: 1.0,
18
- filter: function (x) {
19
- if (x <= -1.0 || x >= 1.0) { return 0.0; }
20
- if (x > -1.19209290E-07 && x < 1.19209290E-07) { return 1.0; }
21
+ fn: function (x) {
22
+ if (x < 0) x = -x;
23
+ if (x >= 1.0) { return 0.0; }
24
+ if (x < 1.19209290E-07) { return 1.0; }
21
25
  var xpi = x * Math.PI;
22
26
  return ((Math.sin(xpi) / xpi) * (0.54 + 0.46 * Math.cos(xpi / 1.0)));
23
27
  }
24
28
  },
25
- { // Lanczos, win = 2
29
+ // Lanczos, win = 2
30
+ lanczos2: {
26
31
  win: 2.0,
27
- filter: function (x) {
28
- if (x <= -2.0 || x >= 2.0) { return 0.0; }
29
- if (x > -1.19209290E-07 && x < 1.19209290E-07) { return 1.0; }
32
+ fn: function (x) {
33
+ if (x < 0) x = -x;
34
+ if (x >= 2.0) { return 0.0; }
35
+ if (x < 1.19209290E-07) { return 1.0; }
30
36
  var xpi = x * Math.PI;
31
37
  return (Math.sin(xpi) / xpi) * Math.sin(xpi / 2.0) / (xpi / 2.0);
32
38
  }
33
39
  },
34
- { // Lanczos, win = 3
40
+ // Lanczos, win = 3
41
+ lanczos3: {
35
42
  win: 3.0,
36
- filter: function (x) {
37
- if (x <= -3.0 || x >= 3.0) { return 0.0; }
38
- if (x > -1.19209290E-07 && x < 1.19209290E-07) { return 1.0; }
43
+ fn: function (x) {
44
+ if (x < 0) x = -x;
45
+ if (x >= 3.0) { return 0.0; }
46
+ if (x < 1.19209290E-07) { return 1.0; }
39
47
  var xpi = x * Math.PI;
40
48
  return (Math.sin(xpi) / xpi) * Math.sin(xpi / 3.0) / (xpi / 3.0);
41
49
  }
50
+ },
51
+ // Magic Kernel Sharp 2013, win = 2.5
52
+ // http://johncostella.com/magic/
53
+ mks2013: {
54
+ win: 2.5,
55
+ fn: function (x) {
56
+ if (x < 0) x = -x;
57
+ if (x >= 2.5) { return 0.0; }
58
+ if (x >= 1.5) { return -0.125 * (x - 2.5) * (x - 2.5); }
59
+ if (x >= 0.5) { return 0.25 * (4 * x * x - 11 * x + 7); }
60
+ return 1.0625 - 1.75 * x * x;
61
+ }
42
62
  }
43
- ];
63
+ };
64
+
65
+ module.exports = {
66
+ filter: filter,
67
+ // Legacy mapping
68
+ f2q: { box: 0, hamming: 1, lanczos2: 2, lanczos3: 3 },
69
+ q2f: [ 'box', 'hamming', 'lanczos2', 'lanczos3' ]
70
+ };