imagescript 3.2.0 → 3.2.1

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/animation.js CHANGED
@@ -13,7 +13,7 @@ const fromPlays = (plays) => plays === 0 ? -1 : plays - 1;
13
13
  function canvasFrames(width, height, frames) {
14
14
  return frames.map(({ pixels, duration }) => {
15
15
  const frame = new Frame(width, height, duration, 0, 0, Frame.DISPOSAL_BACKGROUND);
16
- frame.bitmap.set(pixels);
16
+ frame.bitmap = pixels;
17
17
  return frame;
18
18
  });
19
19
  }
@@ -188,23 +188,20 @@ export function decode(data, onlyFirstFrame = false) {
188
188
  control = null;
189
189
  if (!fw || !fh)
190
190
  continue;
191
- const pixels = new Uint8Array(fw * fh * 4);
192
- const rows = interlaced ? interlacedRows(fh) : null;
193
- const colors = new Uint8Array(256 * 4);
191
+ const colors = new Uint32Array(256), bytes = new Uint8Array(colors.buffer);
194
192
  for (let index = 0; index < 256; index++) {
195
- if (index === transparentIndex)
196
- continue;
197
193
  if (table && index * 3 + 2 < table.length)
198
- colors.set(table.subarray(index * 3, index * 3 + 3), index * 4);
199
- colors[index * 4 + 3] = 0xff;
194
+ bytes.set(table.subarray(index * 3, index * 3 + 3), index * 4);
195
+ bytes[index * 4 + 3] = 0xff;
200
196
  }
201
- const lookup = new Uint32Array(colors.buffer), out = new Uint32Array(pixels.buffer);
202
- for (let row = 0; row < fh; row++) {
203
- const target = (rows ? rows[row] : row) * fw, source = row * fw;
204
- for (let column = 0; column < fw; column++)
205
- out[target + column] = lookup[indices[source + column]];
197
+ let ordered = indices;
198
+ if (interlaced) {
199
+ const rows = interlacedRows(fh);
200
+ ordered = new Uint8Array(indices.length);
201
+ for (let row = 0; row < fh; row++)
202
+ ordered.set(indices.subarray(row * fw, (row + 1) * fw), rows[row] * fw);
206
203
  }
207
- frames.push({ x, y, width: fw, height: fh, delay, dispose, pixels });
204
+ frames.push({ x, y, width: fw, height: fh, delay, dispose, indices: ordered, colors, transparentIndex });
208
205
  if (onlyFirstFrame)
209
206
  break;
210
207
  }
@@ -215,15 +212,21 @@ export function composite({ width, height, frames }) {
215
212
  const output = [];
216
213
  for (const frame of frames) {
217
214
  const previous = frame.dispose === 2 ? canvas.slice() : null;
218
- const src = new Uint32Array(frame.pixels.buffer, frame.pixels.byteOffset, frame.width * frame.height);
215
+ const { indices, colors, transparentIndex } = frame;
219
216
  const x0 = Math.min(frame.x, width), x1 = Math.min(frame.x + frame.width, width);
220
217
  const y0 = Math.min(frame.y, height), y1 = Math.min(frame.y + frame.height, height);
221
218
  for (let y = y0; y < y1; y++) {
222
219
  const srcRow = (y - frame.y) * frame.width - frame.x;
223
220
  const dstRow = y * width;
224
- for (let x = x0; x < x1; x++)
225
- if (frame.pixels[(srcRow + x) * 4 + 3] !== 0)
226
- canvas[dstRow + x] = src[srcRow + x];
221
+ if (transparentIndex < 0)
222
+ for (let x = x0; x < x1; x++)
223
+ canvas[dstRow + x] = colors[indices[srcRow + x]];
224
+ else
225
+ for (let x = x0; x < x1; x++) {
226
+ const index = indices[srcRow + x];
227
+ if (index !== transparentIndex)
228
+ canvas[dstRow + x] = colors[index];
229
+ }
227
230
  }
228
231
  output.push({ pixels: new Uint8Array(canvas.slice().buffer), delay: frame.delay });
229
232
  if (frame.dispose === 3) {
package/dist/image.js CHANGED
@@ -12,7 +12,7 @@ import { TextLayout } from "./text-layout.js";
12
12
  import { resolveMetadata, pngText, exifData } from "./metadata.js";
13
13
  import { encode as utf8 } from "./util/utf8.js";
14
14
  import { resample, BOX, TRIANGLE, CUBIC } from "./util/resample.js";
15
- import { gaussianBlur, premultiply, unpremultiply } from "./util/blur.js";
15
+ import { gaussianBlur, boxBlurRGBA, premultiply, unpremultiply } from "./util/blur.js";
16
16
  import { isOptions } from "./util/options.js";
17
17
  export class Image {
18
18
  __width__;
@@ -189,8 +189,10 @@ export class Image {
189
189
  return this;
190
190
  }
191
191
  clone() {
192
- const image = new Image(this.width, this.height);
193
- image.bitmap.set(this.bitmap);
192
+ const image = new Image(1, 1);
193
+ image.__width__ = this.width;
194
+ image.__height__ = this.height;
195
+ image.__set_bitmap__(new Uint8ClampedArray(this.bitmap));
194
196
  return image;
195
197
  }
196
198
  static get RESIZE_NEAREST_NEIGHBOR() {
@@ -612,6 +614,8 @@ export class Image {
612
614
  blur(radius) {
613
615
  if (isNaN(radius) || radius < 0)
614
616
  throw new RangeError('Invalid blur radius');
617
+ if (boxBlurRGBA(this.bitmap, this.width, this.height, radius))
618
+ return this;
615
619
  const pixels = premultiply(this.bitmap);
616
620
  gaussianBlur(pixels, this.width, this.height, radius);
617
621
  unpremultiply(pixels, this.bitmap);
package/dist/util/blur.js CHANGED
@@ -119,6 +119,94 @@ export function gaussianBlur(pixels, width, height, sigma) {
119
119
  boxPassRows(tmp, pixels, width, height, radius);
120
120
  }
121
121
  }
122
+ function boxRowWords(src, dst, s, length, radius, mult) {
123
+ const last = length - 1;
124
+ const first = src[s], end = src[s + last];
125
+ let rb = (radius + 1) * (first & 0xff00ff), ga = (radius + 1) * (first >>> 8 & 0xff00ff);
126
+ for (let k = 1; k <= radius; k++) {
127
+ const p = src[s + (k < last ? k : last)];
128
+ rb += p & 0xff00ff;
129
+ ga += p >>> 8 & 0xff00ff;
130
+ }
131
+ for (let x = 0; x < length; x++) {
132
+ dst[s + x] = (Math.imul(rb & 0xffff, mult) + 32768 >>> 16) | (Math.imul(ga & 0xffff, mult) + 32768 >>> 16) << 8
133
+ | (Math.imul(rb >>> 16, mult) + 32768 >>> 16) << 16 | (Math.imul(ga >>> 16, mult) + 32768 >>> 16) << 24;
134
+ const add = x + radius + 1, remove = x - radius;
135
+ const entering = add < last ? src[s + add] : end, leaving = remove > 0 ? src[s + remove] : first;
136
+ rb += (entering & 0xff00ff) - (leaving & 0xff00ff);
137
+ ga += (entering >>> 8 & 0xff00ff) - (leaving >>> 8 & 0xff00ff);
138
+ }
139
+ }
140
+ function boxColumnsWords(src, dst, width, height, radius, mult, rbs, gas) {
141
+ for (let x = 0; x < width; x++) {
142
+ const p = src[x];
143
+ rbs[x] = (radius + 1) * (p & 0xff00ff);
144
+ gas[x] = (radius + 1) * (p >>> 8 & 0xff00ff);
145
+ }
146
+ for (let k = 1; k <= radius; k++) {
147
+ const o = Math.min(k, height - 1) * width;
148
+ for (let x = 0; x < width; x++) {
149
+ const p = src[o + x];
150
+ rbs[x] += p & 0xff00ff;
151
+ gas[x] += p >>> 8 & 0xff00ff;
152
+ }
153
+ }
154
+ for (let y = 0; y < height; y++) {
155
+ const o = y * width, add = Math.min(height - 1, y + radius + 1) * width, remove = Math.max(0, y - radius) * width;
156
+ for (let x = 0; x < width; x++) {
157
+ const rb = rbs[x], ga = gas[x];
158
+ dst[o + x] = (Math.imul(rb & 0xffff, mult) + 32768 >>> 16) | (Math.imul(ga & 0xffff, mult) + 32768 >>> 16) << 8
159
+ | (Math.imul(rb >>> 16, mult) + 32768 >>> 16) << 16 | (Math.imul(ga >>> 16, mult) + 32768 >>> 16) << 24;
160
+ const entering = src[add + x], leaving = src[remove + x];
161
+ rbs[x] = rb + (entering & 0xff00ff) - (leaving & 0xff00ff);
162
+ gas[x] = ga + (entering >>> 8 & 0xff00ff) - (leaving >>> 8 & 0xff00ff);
163
+ }
164
+ }
165
+ }
166
+ export function boxBlurRGBA(bitmap, width, height, sigma) {
167
+ const sizes = boxSizes(sigma);
168
+ if (!(sigma >= 2) || Math.max(...sizes) > 257)
169
+ return false;
170
+ let opaque = true;
171
+ for (let i = 3; i < bitmap.length; i += 4)
172
+ if (bitmap[i] !== 255) {
173
+ opaque = false;
174
+ break;
175
+ }
176
+ if (!opaque) {
177
+ for (let i = 0; i < bitmap.length; i += 4) {
178
+ const a = bitmap[i + 3];
179
+ bitmap[i] = (bitmap[i] * a + 128) * 257 >> 16;
180
+ bitmap[i + 1] = (bitmap[i + 1] * a + 128) * 257 >> 16;
181
+ bitmap[i + 2] = (bitmap[i + 2] * a + 128) * 257 >> 16;
182
+ }
183
+ }
184
+ const pixels = new Int32Array(bitmap.buffer, bitmap.byteOffset, width * height);
185
+ const tmp = new Int32Array(pixels.length), rbs = new Int32Array(width), gas = new Int32Array(width);
186
+ for (const size of sizes) {
187
+ const radius = (size - 1) / 2;
188
+ if (radius < 1)
189
+ continue;
190
+ const mult = Math.round(65536 / size);
191
+ for (let y = 0; y < height; y++)
192
+ boxRowWords(pixels, tmp, y * width, width, radius, mult);
193
+ boxColumnsWords(tmp, pixels, width, height, radius, mult, rbs, gas);
194
+ }
195
+ if (!opaque) {
196
+ for (let i = 0; i < bitmap.length; i += 4) {
197
+ const a = bitmap[i + 3];
198
+ if (a === 0) {
199
+ bitmap[i] = bitmap[i + 1] = bitmap[i + 2] = 0;
200
+ continue;
201
+ }
202
+ const f = 255 / a;
203
+ bitmap[i] *= f;
204
+ bitmap[i + 1] *= f;
205
+ bitmap[i + 2] *= f;
206
+ }
207
+ }
208
+ return true;
209
+ }
122
210
  export function premultiply(rgba) {
123
211
  const out = new Float32Array(rgba.length);
124
212
  for (let i = 0; i < rgba.length; i += 4) {
@@ -12,106 +12,269 @@ export const CUBIC = {
12
12
  return 0;
13
13
  }
14
14
  };
15
- function coefficients(inSize, outSize, filter) {
15
+ const BITS = 14;
16
+ const ONE = 1 << BITS;
17
+ function coefficients(inSize, outSize, filter, bits) {
16
18
  const scale = inSize / outSize;
17
19
  const filterScale = Math.max(1, scale);
18
20
  const support = filter.support * filterScale;
19
- const size = Math.ceil(support) * 2 + 1;
20
- const start = new Int32Array(outSize);
21
- const count = new Int32Array(outSize);
22
- const weights = new Float64Array(outSize * size);
21
+ const one = 1 << bits;
22
+ const mins = new Int32Array(outSize), maxs = new Int32Array(outSize);
23
+ let taps = 0;
23
24
  for (let i = 0; i < outSize; i++) {
24
25
  const center = (i + 0.5) * scale;
25
- const min = Math.max(0, Math.floor(center - support + 0.5));
26
- const max = Math.min(inSize, Math.floor(center + support + 0.5));
26
+ mins[i] = Math.max(0, Math.floor(center - support + 0.5));
27
+ maxs[i] = Math.min(inSize, Math.floor(center + support + 0.5));
28
+ taps = Math.max(taps, maxs[i] - mins[i]);
29
+ }
30
+ const start = new Int32Array(outSize);
31
+ const weights = new Int32Array(outSize * taps);
32
+ const w = new Float64Array(taps);
33
+ for (let i = 0; i < outSize; i++) {
34
+ const center = (i + 0.5) * scale, min = mins[i], n = maxs[i] - min;
27
35
  let total = 0;
28
- for (let x = min; x < max; x++) {
29
- const w = filter.weight((x - center + 0.5) / filterScale);
30
- weights[i * size + x - min] = w;
31
- total += w;
36
+ for (let k = 0; k < n; k++)
37
+ total += w[k] = filter.weight((min + k - center + 0.5) / filterScale);
38
+ const shift = Math.max(0, min + taps - inSize), o = i * taps + shift;
39
+ start[i] = min - shift;
40
+ let sum = 0, largest = 0;
41
+ for (let k = 0; k < n; k++) {
42
+ sum += weights[o + k] = Math.round(total ? w[k] / total * one : 0);
43
+ if (Math.abs(w[k]) > Math.abs(w[largest]))
44
+ largest = k;
32
45
  }
33
- if (total !== 0)
34
- for (let k = 0; k < max - min; k++)
35
- weights[i * size + k] /= total;
36
- start[i] = min;
37
- count[i] = max - min;
46
+ if (n)
47
+ weights[o + largest] += one - sum;
38
48
  }
39
- return { start, count, weights, size };
49
+ return { start, weights, taps };
40
50
  }
51
+ const littleEndian = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
41
52
  export function resample(src, width, height, outWidth, outHeight, filter, out) {
42
- let plane = new Float32Array(outWidth * height * 4);
43
- if (outWidth !== width) {
44
- const { start, count, weights, size } = coefficients(width, outWidth, filter);
45
- for (let y = 0, o = 0; y < height; y++) {
46
- const row = y * width * 4;
47
- for (let x = 0; x < outWidth; x++, o += 4) {
48
- let r = 0, g = 0, b = 0, a = 0;
49
- for (let k = 0, s = row + start[x] * 4, w = x * size, n = count[x]; k < n; k++, s += 4) {
53
+ if (littleEndian && filter.support <= 1 && resampleOpaque(src, width, height, outWidth, outHeight, filter, out))
54
+ return;
55
+ resamplePremultiplied(src, width, height, outWidth, outHeight, filter, out);
56
+ }
57
+ function resampleOpaque(src, width, height, outWidth, outHeight, filter, out) {
58
+ const pixels = new Int32Array(src.buffer, src.byteOffset, width * height);
59
+ let all = -1;
60
+ for (let x = 0, last = (height - 1) * width; x < width; x++)
61
+ all &= pixels[x] & pixels[last + x];
62
+ if (all >>> 24 !== 255)
63
+ return false;
64
+ const lanes = new Int32Array(width * outHeight * 2);
65
+ if (outHeight === height) {
66
+ for (let i = 0, o = 0; i < pixels.length; i++, o += 2) {
67
+ const p = pixels[i];
68
+ all &= p;
69
+ lanes[o] = (p & 0xff00ff) << 8;
70
+ lanes[o + 1] = (p & 0xff00) | 0;
71
+ }
72
+ }
73
+ else {
74
+ let coefficientsV = coefficients(height, outHeight, filter, 8);
75
+ const swar = coefficientsV.taps <= 4;
76
+ if (!swar)
77
+ coefficientsV = coefficients(height, outHeight, filter, BITS);
78
+ const { start, weights, taps } = coefficientsV;
79
+ for (let y = 0, o = 0; y < outHeight; y++) {
80
+ const w = y * taps, r0 = start[y] * width;
81
+ if (swar && taps === 4) {
82
+ const w0 = weights[w], w1 = weights[w + 1], w2 = weights[w + 2], w3 = weights[w + 3];
83
+ const r1 = r0 + width, r2 = r1 + width, r3 = r2 + width;
84
+ for (let x = 0; x < width; x++, o += 2) {
85
+ const p0 = pixels[r0 + x], p1 = pixels[r1 + x], p2 = pixels[r2 + x], p3 = pixels[r3 + x];
86
+ all &= p0 & p1 & p2 & p3;
87
+ lanes[o] = Math.imul(p0 & 0xff00ff, w0) + Math.imul(p1 & 0xff00ff, w1) + Math.imul(p2 & 0xff00ff, w2) + Math.imul(p3 & 0xff00ff, w3);
88
+ lanes[o + 1] = (p0 >> 8 & 255) * w0 + (p1 >> 8 & 255) * w1 + (p2 >> 8 & 255) * w2 + (p3 >> 8 & 255) * w3;
89
+ }
90
+ }
91
+ else if (swar && taps === 3) {
92
+ const w0 = weights[w], w1 = weights[w + 1], w2 = weights[w + 2], r1 = r0 + width, r2 = r1 + width;
93
+ for (let x = 0; x < width; x++, o += 2) {
94
+ const p0 = pixels[r0 + x], p1 = pixels[r1 + x], p2 = pixels[r2 + x];
95
+ all &= p0 & p1 & p2;
96
+ lanes[o] = Math.imul(p0 & 0xff00ff, w0) + Math.imul(p1 & 0xff00ff, w1) + Math.imul(p2 & 0xff00ff, w2);
97
+ lanes[o + 1] = (p0 >> 8 & 255) * w0 + (p1 >> 8 & 255) * w1 + (p2 >> 8 & 255) * w2;
98
+ }
99
+ }
100
+ else if (swar && taps === 2) {
101
+ const w0 = weights[w], w1 = weights[w + 1], r1 = r0 + width;
102
+ for (let x = 0; x < width; x++, o += 2) {
103
+ const p0 = pixels[r0 + x], p1 = pixels[r1 + x];
104
+ all &= p0 & p1;
105
+ lanes[o] = Math.imul(p0 & 0xff00ff, w0) + Math.imul(p1 & 0xff00ff, w1);
106
+ lanes[o + 1] = (p0 >> 8 & 255) * w0 + (p1 >> 8 & 255) * w1;
107
+ }
108
+ }
109
+ else if (swar) {
110
+ for (let x = 0; x < width; x++, o += 2) {
111
+ let rb = 0, g = 0;
112
+ for (let k = 0, s = r0 + x; k < taps; k++, s += width) {
113
+ const p = pixels[s], wk = weights[w + k];
114
+ all &= p;
115
+ rb += Math.imul(p & 0xff00ff, wk);
116
+ g += (p >> 8 & 255) * wk;
117
+ }
118
+ lanes[o] = rb;
119
+ lanes[o + 1] = g;
120
+ }
121
+ }
122
+ else {
123
+ for (let x = 0; x < width; x++, o += 2) {
124
+ let r = 0, g = 0, b = 0, k = 0, s = r0 + x;
125
+ for (; k + 1 < taps; k += 2, s += width * 2) {
126
+ const p = pixels[s], q = pixels[s + width], w0 = weights[w + k], w1 = weights[w + k + 1];
127
+ all &= p & q;
128
+ r += (p & 255) * w0 + (q & 255) * w1;
129
+ g += (p >> 8 & 255) * w0 + (q >> 8 & 255) * w1;
130
+ b += (p >> 16 & 255) * w0 + (q >> 16 & 255) * w1;
131
+ }
132
+ if (k < taps) {
133
+ const p = pixels[s], w0 = weights[w + k];
134
+ all &= p;
135
+ r += (p & 255) * w0;
136
+ g += (p >> 8 & 255) * w0;
137
+ b += (p >> 16 & 255) * w0;
138
+ }
139
+ lanes[o] = (r + 32 >> 6) | (b + 32 >> 6) << 16;
140
+ lanes[o + 1] = g + 32 >> 6;
141
+ }
142
+ }
143
+ }
144
+ }
145
+ if (all >>> 24 !== 255)
146
+ return false;
147
+ const result = new Int32Array(out.buffer, out.byteOffset, outWidth * outHeight);
148
+ if (outWidth === width) {
149
+ for (let i = 0, o = 0; i < result.length; i++, o += 2) {
150
+ const rb = lanes[o], g = lanes[o + 1];
151
+ result[i] = ((rb & 0xffff) + 128 >> 8) | (g + 128 >> 8) << 8 | ((rb >>> 16) + 128 >> 8) << 16 | 0xff000000;
152
+ }
153
+ return true;
154
+ }
155
+ const R = 1 << 7 + BITS, S = 8 + BITS;
156
+ const { start, weights, taps } = coefficients(width, outWidth, filter, BITS);
157
+ for (let y = 0, o = 0; y < outHeight; y++) {
158
+ const row = y * width * 2;
159
+ if (taps === 4) {
160
+ for (let x = 0; x < outWidth; x++, o++) {
161
+ const s = row + start[x] * 2, w = x * 4;
162
+ const a0 = lanes[s], a1 = lanes[s + 2], a2 = lanes[s + 4], a3 = lanes[s + 6];
163
+ const w0 = weights[w], w1 = weights[w + 1], w2 = weights[w + 2], w3 = weights[w + 3];
164
+ const r = (a0 & 0xffff) * w0 + (a1 & 0xffff) * w1 + (a2 & 0xffff) * w2 + (a3 & 0xffff) * w3;
165
+ const b = (a0 >>> 16) * w0 + (a1 >>> 16) * w1 + (a2 >>> 16) * w2 + (a3 >>> 16) * w3;
166
+ const g = lanes[s + 1] * w0 + lanes[s + 3] * w1 + lanes[s + 5] * w2 + lanes[s + 7] * w3;
167
+ result[o] = (r + R >> S) | (g + R >> S) << 8 | (b + R >> S) << 16 | 0xff000000;
168
+ }
169
+ }
170
+ else if (taps === 2) {
171
+ for (let x = 0; x < outWidth; x++, o++) {
172
+ const s = row + start[x] * 2, w = x * 2;
173
+ const a0 = lanes[s], a1 = lanes[s + 2], w0 = weights[w], w1 = weights[w + 1];
174
+ const r = (a0 & 0xffff) * w0 + (a1 & 0xffff) * w1;
175
+ const b = (a0 >>> 16) * w0 + (a1 >>> 16) * w1;
176
+ const g = lanes[s + 1] * w0 + lanes[s + 3] * w1;
177
+ result[o] = (r + R >> S) | (g + R >> S) << 8 | (b + R >> S) << 16 | 0xff000000;
178
+ }
179
+ }
180
+ else {
181
+ for (let x = 0; x < outWidth; x++, o++) {
182
+ let r = 0, g = 0, b = 0, k = 0, s = row + start[x] * 2, w = x * taps;
183
+ for (; k + 1 < taps; k += 2, s += 4) {
184
+ const a0 = lanes[s], a1 = lanes[s + 2], w0 = weights[w + k], w1 = weights[w + k + 1];
185
+ r += (a0 & 0xffff) * w0 + (a1 & 0xffff) * w1;
186
+ b += (a0 >>> 16) * w0 + (a1 >>> 16) * w1;
187
+ g += lanes[s + 1] * w0 + lanes[s + 3] * w1;
188
+ }
189
+ if (k < taps) {
190
+ const a0 = lanes[s], w0 = weights[w + k];
191
+ r += (a0 & 0xffff) * w0;
192
+ b += (a0 >>> 16) * w0;
193
+ g += lanes[s + 1] * w0;
194
+ }
195
+ result[o] = (r + R >> S) | (g + R >> S) << 8 | (b + R >> S) << 16 | 0xff000000;
196
+ }
197
+ }
198
+ }
199
+ return true;
200
+ }
201
+ function resamplePremultiplied(src, width, height, outWidth, outHeight, filter, out) {
202
+ const plane = new Int32Array(width * outHeight * 4);
203
+ if (outHeight === height) {
204
+ for (let s = 0; s < src.length; s += 4) {
205
+ const a = src[s + 3];
206
+ plane[s] = src[s] * a;
207
+ plane[s + 1] = src[s + 1] * a;
208
+ plane[s + 2] = src[s + 2] * a;
209
+ plane[s + 3] = a * 255;
210
+ }
211
+ }
212
+ else {
213
+ const { start, weights, taps } = coefficients(height, outHeight, filter, BITS);
214
+ const stride = width * 4, H = ONE >> 1;
215
+ for (let y = 0, o = 0; y < outHeight; y++) {
216
+ const w = y * taps, r0 = start[y] * stride;
217
+ for (let x = 0; x < stride; x += 4, o += 4) {
218
+ let r = 0, g = 0, b = 0, a = 0, k = 0, s = r0 + x;
219
+ for (; k + 1 < taps; k += 2, s += stride * 2) {
220
+ const t = s + stride, wa = weights[w + k] * src[s + 3], wb = weights[w + k + 1] * src[t + 3];
221
+ r += src[s] * wa + src[t] * wb;
222
+ g += src[s + 1] * wa + src[t + 1] * wb;
223
+ b += src[s + 2] * wa + src[t + 2] * wb;
224
+ a += wa + wb;
225
+ }
226
+ if (k < taps) {
50
227
  const wa = weights[w + k] * src[s + 3];
51
228
  r += src[s] * wa;
52
229
  g += src[s + 1] * wa;
53
230
  b += src[s + 2] * wa;
54
231
  a += wa;
55
232
  }
56
- plane[o] = r / 255;
57
- plane[o + 1] = g / 255;
58
- plane[o + 2] = b / 255;
59
- plane[o + 3] = a;
233
+ plane[o] = r + H >> BITS;
234
+ plane[o + 1] = g + H >> BITS;
235
+ plane[o + 2] = b + H >> BITS;
236
+ plane[o + 3] = a * 255 + H >> BITS;
60
237
  }
61
238
  }
62
239
  }
63
- else {
64
- for (let s = 0; s < plane.length; s += 4) {
65
- const a = src[s + 3];
66
- plane[s] = src[s] * a / 255;
67
- plane[s + 1] = src[s + 1] * a / 255;
68
- plane[s + 2] = src[s + 2] * a / 255;
69
- plane[s + 3] = a;
70
- }
71
- }
72
- if (outHeight !== height) {
73
- const { start, count, weights, size } = coefficients(height, outHeight, filter);
74
- const next = new Float32Array(outWidth * outHeight * 4);
75
- const stride = outWidth * 4;
76
- for (let y = 0; y < outHeight; y++) {
77
- const o = y * stride, n = count[y], w = y * size;
78
- const s0 = start[y] * stride, s1 = s0 + stride, s2 = s1 + stride, s3 = s2 + stride;
79
- const w0 = weights[w], w1 = weights[w + 1], w2 = weights[w + 2], w3 = weights[w + 3];
80
- if (n === 1)
81
- for (let x = 0; x < stride; x++)
82
- next[o + x] = 0 + plane[s0 + x] * w0;
83
- else if (n === 2)
84
- for (let x = 0; x < stride; x++)
85
- next[o + x] = Math.fround(0 + plane[s0 + x] * w0) + plane[s1 + x] * w1;
86
- else if (n === 3) {
87
- for (let x = 0; x < stride; x++)
88
- next[o + x] = Math.fround(Math.fround(0 + plane[s0 + x] * w0) + plane[s1 + x] * w1) + plane[s2 + x] * w2;
89
- }
90
- else if (n === 4) {
91
- for (let x = 0; x < stride; x++)
92
- next[o + x] = Math.fround(Math.fround(Math.fround(0 + plane[s0 + x] * w0) + plane[s1 + x] * w1) + plane[s2 + x] * w2) + plane[s3 + x] * w3;
240
+ const { start, weights, taps } = outWidth === width ? { start: null, weights: null, taps: 0 } : coefficients(width, outWidth, filter, BITS);
241
+ for (let y = 0, o = 0; y < outHeight; y++) {
242
+ const row = y * width * 4;
243
+ for (let x = 0; x < outWidth; x++, o += 4) {
244
+ let r = 0, g = 0, b = 0, a = 0;
245
+ if (!start || !weights) {
246
+ const s = row + x * 4;
247
+ r = plane[s] * ONE;
248
+ g = plane[s + 1] * ONE;
249
+ b = plane[s + 2] * ONE;
250
+ a = plane[s + 3] * ONE;
93
251
  }
94
252
  else {
95
- for (let x = 0; x < stride; x++) {
96
- let sum = 0;
97
- for (let k = 0, s = s0 + x; k < n; k++, s += stride)
98
- sum = Math.fround(sum + plane[s] * weights[w + k]);
99
- next[o + x] = sum;
253
+ let k = 0, s = row + start[x] * 4, w = x * taps;
254
+ for (; k + 1 < taps; k += 2, s += 8) {
255
+ const w0 = weights[w + k], w1 = weights[w + k + 1];
256
+ r += plane[s] * w0 + plane[s + 4] * w1;
257
+ g += plane[s + 1] * w0 + plane[s + 5] * w1;
258
+ b += plane[s + 2] * w0 + plane[s + 6] * w1;
259
+ a += plane[s + 3] * w0 + plane[s + 7] * w1;
260
+ }
261
+ if (k < taps) {
262
+ const w0 = weights[w + k];
263
+ r += plane[s] * w0;
264
+ g += plane[s + 1] * w0;
265
+ b += plane[s + 2] * w0;
266
+ a += plane[s + 3] * w0;
100
267
  }
101
268
  }
269
+ if (a < 127.5 * ONE) {
270
+ out[o] = out[o + 1] = out[o + 2] = out[o + 3] = 0;
271
+ continue;
272
+ }
273
+ const f = 255 / Math.min(a, 65025 * ONE);
274
+ out[o] = r * f;
275
+ out[o + 1] = g * f;
276
+ out[o + 2] = b * f;
277
+ out[o + 3] = a / (255 * ONE);
102
278
  }
103
- plane = next;
104
- }
105
- for (let i = 0; i < plane.length; i += 4) {
106
- const a = plane[i + 3];
107
- if (a < 0.5) {
108
- out[i] = out[i + 1] = out[i + 2] = out[i + 3] = 0;
109
- continue;
110
- }
111
- const f = 255 / Math.min(255, a);
112
- out[i] = plane[i] * f;
113
- out[i + 1] = plane[i + 1] * f;
114
- out[i + 2] = plane[i + 2] * f;
115
- out[i + 3] = a;
116
279
  }
117
280
  }
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '3.2.0';
1
+ export const VERSION = '3.2.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imagescript",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "zero-dependency javascript image manipulation",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",