@pixerate/editor 0.94.0 → 0.96.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.
Files changed (41) hide show
  1. package/THIRD_PARTY_NOTICES.md +2 -0
  2. package/dist/{chunk-ZOOEX3WT.cjs → chunk-CRMAXIUV.cjs} +369 -7
  3. package/dist/chunk-CRMAXIUV.cjs.map +1 -0
  4. package/dist/{chunk-D4ZCU72J.js → chunk-HEW6KDNI.js} +366 -4
  5. package/dist/chunk-HEW6KDNI.js.map +1 -0
  6. package/dist/{chunk-ISDCJHTO.js → chunk-IB53LVGV.js} +238 -285
  7. package/dist/chunk-IB53LVGV.js.map +1 -0
  8. package/dist/{chunk-747CGOT2.js → chunk-KFTQ2PKP.js} +29 -2
  9. package/dist/chunk-KFTQ2PKP.js.map +1 -0
  10. package/dist/{chunk-73L6TROJ.cjs → chunk-T422YCHR.cjs} +71 -44
  11. package/dist/chunk-T422YCHR.cjs.map +1 -0
  12. package/dist/{chunk-4F3YDIWW.cjs → chunk-ZZOTHJ4K.cjs} +264 -311
  13. package/dist/chunk-ZZOTHJ4K.cjs.map +1 -0
  14. package/dist/image-editor/index.cjs +1832 -632
  15. package/dist/image-editor/index.cjs.map +1 -1
  16. package/dist/image-editor/index.d.cts +179 -22
  17. package/dist/image-editor/index.d.ts +179 -22
  18. package/dist/image-editor/index.js +1463 -263
  19. package/dist/image-editor/index.js.map +1 -1
  20. package/dist/paint/index.cjs +22 -3
  21. package/dist/paint/index.cjs.map +1 -1
  22. package/dist/paint/index.d.cts +117 -2
  23. package/dist/paint/index.d.ts +117 -2
  24. package/dist/paint/index.js +32 -13
  25. package/dist/raster/index.cjs +7 -3
  26. package/dist/raster/index.cjs.map +1 -1
  27. package/dist/raster/index.d.cts +796 -5
  28. package/dist/raster/index.d.ts +796 -5
  29. package/dist/raster/index.js +6 -2
  30. package/dist/{surface-CuBg2c6k.d.cts → surface-DIA-XatS.d.cts} +23 -11
  31. package/dist/{surface-CuBg2c6k.d.ts → surface-DIA-XatS.d.ts} +23 -11
  32. package/package.json +1 -1
  33. package/skills/pixerate-image-editor/SKILL.md +39 -6
  34. package/dist/chunk-4F3YDIWW.cjs.map +0 -1
  35. package/dist/chunk-73L6TROJ.cjs.map +0 -1
  36. package/dist/chunk-747CGOT2.js.map +0 -1
  37. package/dist/chunk-D4ZCU72J.js.map +0 -1
  38. package/dist/chunk-ISDCJHTO.js.map +0 -1
  39. package/dist/chunk-ZOOEX3WT.cjs.map +0 -1
  40. package/dist/zlib-BJ6U7LuB.d.cts +0 -795
  41. package/dist/zlib-BjQkpMFi.d.ts +0 -795
@@ -1,9 +1,14 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class; var _class2; var _class3;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4;
2
2
 
3
3
 
4
4
 
5
5
 
6
- var _chunkZOOEX3WTcjs = require('./chunk-ZOOEX3WT.cjs');
6
+
7
+
8
+
9
+
10
+
11
+ var _chunkCRMAXIUVcjs = require('./chunk-CRMAXIUV.cjs');
7
12
 
8
13
  // src/paint/brush.ts
9
14
  var DEFAULT_BRUSH = {
@@ -216,41 +221,41 @@ function rasterizeDab(d, hardness, rect, out) {
216
221
  }
217
222
  }
218
223
  }
219
- var TILE_PIXELS = _chunkZOOEX3WTcjs.TILE * _chunkZOOEX3WTcjs.TILE;
224
+ var TILE_PIXELS = _chunkCRMAXIUVcjs.TILE * _chunkCRMAXIUVcjs.TILE;
220
225
  var CoverageMap = (_class2 = class {constructor() { _class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this); }
221
226
  __init4() {this.tiles = /* @__PURE__ */ new Map()}
222
227
  __init5() {this.bounds = null}
223
228
  __init6() {this.dirty = /* @__PURE__ */ new Set()}
224
229
  get(x, y) {
225
- const tx = Math.floor(x / _chunkZOOEX3WTcjs.TILE);
226
- const ty = Math.floor(y / _chunkZOOEX3WTcjs.TILE);
227
- const t = this.tiles.get(_chunkZOOEX3WTcjs.tileKey.call(void 0, tx, ty));
228
- return t ? t[(y - ty * _chunkZOOEX3WTcjs.TILE) * _chunkZOOEX3WTcjs.TILE + (x - tx * _chunkZOOEX3WTcjs.TILE)] : 0;
230
+ const tx = Math.floor(x / _chunkCRMAXIUVcjs.TILE);
231
+ const ty = Math.floor(y / _chunkCRMAXIUVcjs.TILE);
232
+ const t = this.tiles.get(_chunkCRMAXIUVcjs.tileKey.call(void 0, tx, ty));
233
+ return t ? t[(y - ty * _chunkCRMAXIUVcjs.TILE) * _chunkCRMAXIUVcjs.TILE + (x - tx * _chunkCRMAXIUVcjs.TILE)] : 0;
229
234
  }
230
235
  /** Builds up `vals` (over `rect`) with flow towards `ceil`: `c ← c + v·(ceil − c)`. */
231
236
  accumulate(rect, vals, ceil) {
232
237
  if (rect.width <= 0 || rect.height <= 0) return;
233
238
  const w = rect.width;
234
- const tx0 = Math.floor(rect.x / _chunkZOOEX3WTcjs.TILE);
235
- const ty0 = Math.floor(rect.y / _chunkZOOEX3WTcjs.TILE);
236
- const tx1 = Math.floor((rect.x + rect.width - 1) / _chunkZOOEX3WTcjs.TILE);
237
- const ty1 = Math.floor((rect.y + rect.height - 1) / _chunkZOOEX3WTcjs.TILE);
239
+ const tx0 = Math.floor(rect.x / _chunkCRMAXIUVcjs.TILE);
240
+ const ty0 = Math.floor(rect.y / _chunkCRMAXIUVcjs.TILE);
241
+ const tx1 = Math.floor((rect.x + rect.width - 1) / _chunkCRMAXIUVcjs.TILE);
242
+ const ty1 = Math.floor((rect.y + rect.height - 1) / _chunkCRMAXIUVcjs.TILE);
238
243
  let touched = false;
239
244
  for (let ty = ty0; ty <= ty1; ty++) {
240
245
  for (let tx = tx0; tx <= tx1; tx++) {
241
- const x0 = Math.max(rect.x, tx * _chunkZOOEX3WTcjs.TILE);
242
- const y0 = Math.max(rect.y, ty * _chunkZOOEX3WTcjs.TILE);
243
- const x1 = Math.min(rect.x + rect.width, tx * _chunkZOOEX3WTcjs.TILE + _chunkZOOEX3WTcjs.TILE);
244
- const y1 = Math.min(rect.y + rect.height, ty * _chunkZOOEX3WTcjs.TILE + _chunkZOOEX3WTcjs.TILE);
246
+ const x0 = Math.max(rect.x, tx * _chunkCRMAXIUVcjs.TILE);
247
+ const y0 = Math.max(rect.y, ty * _chunkCRMAXIUVcjs.TILE);
248
+ const x1 = Math.min(rect.x + rect.width, tx * _chunkCRMAXIUVcjs.TILE + _chunkCRMAXIUVcjs.TILE);
249
+ const y1 = Math.min(rect.y + rect.height, ty * _chunkCRMAXIUVcjs.TILE + _chunkCRMAXIUVcjs.TILE);
245
250
  let tile;
246
251
  for (let y = y0; y < y1; y++) {
247
252
  const row = (y - rect.y) * w;
248
- const trow = (y - ty * _chunkZOOEX3WTcjs.TILE) * _chunkZOOEX3WTcjs.TILE;
253
+ const trow = (y - ty * _chunkCRMAXIUVcjs.TILE) * _chunkCRMAXIUVcjs.TILE;
249
254
  for (let x = x0; x < x1; x++) {
250
255
  const v = vals[row + (x - rect.x)];
251
256
  if (v <= 0) continue;
252
257
  if (!tile) {
253
- const key = _chunkZOOEX3WTcjs.tileKey.call(void 0, tx, ty);
258
+ const key = _chunkCRMAXIUVcjs.tileKey.call(void 0, tx, ty);
254
259
  tile = this.tiles.get(key);
255
260
  if (!tile) {
256
261
  tile = new Float32Array(TILE_PIXELS);
@@ -259,7 +264,7 @@ var CoverageMap = (_class2 = class {constructor() { _class2.prototype.__init4.ca
259
264
  this.dirty.add(key);
260
265
  touched = true;
261
266
  }
262
- const ti = trow + (x - tx * _chunkZOOEX3WTcjs.TILE);
267
+ const ti = trow + (x - tx * _chunkCRMAXIUVcjs.TILE);
263
268
  const c = tile[ti];
264
269
  if (ceil > c) tile[ti] = c + v * (ceil - c);
265
270
  }
@@ -331,35 +336,35 @@ var StrokeCoverage = (_class3 = class {
331
336
  }, _class3);
332
337
  function applyStrokeToTile(coverage, tileData, channels, tx, ty, s, options = {}) {
333
338
  const out = options.into ? new Uint8ClampedArray(options.into) : new Uint8ClampedArray(tileData);
334
- const ox = tx * _chunkZOOEX3WTcjs.TILE + s.offset.x;
335
- const oy = ty * _chunkZOOEX3WTcjs.TILE + s.offset.y;
339
+ const ox = tx * _chunkCRMAXIUVcjs.TILE + s.offset.x;
340
+ const oy = ty * _chunkCRMAXIUVcjs.TILE + s.offset.y;
336
341
  let x0 = 0;
337
342
  let y0 = 0;
338
- let x1 = _chunkZOOEX3WTcjs.TILE;
339
- let y1 = _chunkZOOEX3WTcjs.TILE;
343
+ let x1 = _chunkCRMAXIUVcjs.TILE;
344
+ let y1 = _chunkCRMAXIUVcjs.TILE;
340
345
  if (options.region) {
341
346
  const r = options.region;
342
347
  x0 = Math.max(0, Math.floor(r.x) - ox);
343
348
  y0 = Math.max(0, Math.floor(r.y) - oy);
344
- x1 = Math.min(_chunkZOOEX3WTcjs.TILE, Math.ceil(r.x + r.width) - ox);
345
- y1 = Math.min(_chunkZOOEX3WTcjs.TILE, Math.ceil(r.y + r.height) - oy);
349
+ x1 = Math.min(_chunkCRMAXIUVcjs.TILE, Math.ceil(r.x + r.width) - ox);
350
+ y1 = Math.min(_chunkCRMAXIUVcjs.TILE, Math.ceil(r.y + r.height) - oy);
346
351
  if (x1 <= x0 || y1 <= y0) return out;
347
352
  if (options.into) {
348
- for (let y = y0; y < y1; y++) out.set(tileData.subarray((y * _chunkZOOEX3WTcjs.TILE + x0) * channels, (y * _chunkZOOEX3WTcjs.TILE + x1) * channels), (y * _chunkZOOEX3WTcjs.TILE + x0) * channels);
353
+ for (let y = y0; y < y1; y++) out.set(tileData.subarray((y * _chunkCRMAXIUVcjs.TILE + x0) * channels, (y * _chunkCRMAXIUVcjs.TILE + x1) * channels), (y * _chunkCRMAXIUVcjs.TILE + x0) * channels);
349
354
  }
350
355
  }
351
356
  const sel = s.selection;
352
357
  const cb = [0, 0, 0, 0];
353
358
  const cs = [s.color[0] / 255, s.color[1] / 255, s.color[2] / 255, s.color[3] / 255];
354
359
  const bg = [s.background[0] / 255, s.background[1] / 255, s.background[2] / 255, s.background[3] / 255];
355
- const aligned = s.offset.x % _chunkZOOEX3WTcjs.TILE === 0 && s.offset.y % _chunkZOOEX3WTcjs.TILE === 0;
356
- const covTile = aligned ? coverage.tiles.get(_chunkZOOEX3WTcjs.tileKey.call(void 0, Math.floor(ox / _chunkZOOEX3WTcjs.TILE), Math.floor(oy / _chunkZOOEX3WTcjs.TILE))) : void 0;
360
+ const aligned = s.offset.x % _chunkCRMAXIUVcjs.TILE === 0 && s.offset.y % _chunkCRMAXIUVcjs.TILE === 0;
361
+ const covTile = aligned ? coverage.tiles.get(_chunkCRMAXIUVcjs.tileKey.call(void 0, Math.floor(ox / _chunkCRMAXIUVcjs.TILE), Math.floor(oy / _chunkCRMAXIUVcjs.TILE))) : void 0;
357
362
  if (aligned && !covTile) return out;
358
363
  for (let y = y0; y < y1; y++) {
359
364
  const dy = oy + y;
360
365
  for (let x = x0; x < x1; x++) {
361
366
  const dx = ox + x;
362
- const c = covTile ? covTile[y * _chunkZOOEX3WTcjs.TILE + x] : coverage.get(dx, dy);
367
+ const c = covTile ? covTile[y * _chunkCRMAXIUVcjs.TILE + x] : coverage.get(dx, dy);
363
368
  if (c <= 0) continue;
364
369
  let k = c * s.opacity;
365
370
  if (sel) {
@@ -368,7 +373,7 @@ function applyStrokeToTile(coverage, tileData, channels, tx, ty, s, options = {}
368
373
  }
369
374
  if (k > 1) k = 1;
370
375
  if (k <= 0) continue;
371
- const p = y * _chunkZOOEX3WTcjs.TILE + x;
376
+ const p = y * _chunkCRMAXIUVcjs.TILE + x;
372
377
  if (channels === 1) {
373
378
  const target = s.mode === "erase" ? s.background[0] : s.color[0];
374
379
  out[p] = Math.round(out[p] + (target - out[p]) * k);
@@ -381,8 +386,20 @@ function applyStrokeToTile(coverage, tileData, channels, tx, ty, s, options = {}
381
386
  }
382
387
  if (s.lockTransparency && out[i + 3] === 0) continue;
383
388
  const src = s.mode === "erase" ? bg : cs;
389
+ if (s.blend === "pigment") {
390
+ const bytes = s.mode === "erase" ? s.background : s.color;
391
+ const as = src[3] * k;
392
+ if (!(as > 0)) continue;
393
+ const ao = as + out[i + 3] / 255 * (1 - as);
394
+ _chunkCRMAXIUVcjs.mixPigment8.call(void 0, out[i], out[i + 1], out[i + 2], bytes[0], bytes[1], bytes[2], as / ao, cb);
395
+ out[i] = Math.round(cb[0] * 255);
396
+ out[i + 1] = Math.round(cb[1] * 255);
397
+ out[i + 2] = Math.round(cb[2] * 255);
398
+ if (!s.lockTransparency) out[i + 3] = Math.round(ao * 255);
399
+ continue;
400
+ }
384
401
  for (let ch = 0; ch < 4; ch++) cb[ch] = out[i + ch] / 255;
385
- const o = _chunkZOOEX3WTcjs.blendPixel.call(void 0, s.blend, cb, src, k);
402
+ const o = _chunkCRMAXIUVcjs.blendPixel.call(void 0, s.blend, cb, src, k);
386
403
  out[i] = Math.round(o[0] * 255);
387
404
  out[i + 1] = Math.round(o[1] * 255);
388
405
  out[i + 2] = Math.round(o[2] * 255);
@@ -392,296 +409,234 @@ function applyStrokeToTile(coverage, tileData, channels, tx, ty, s, options = {}
392
409
  return out;
393
410
  }
394
411
  function coverageTileCoord(key) {
395
- return _chunkZOOEX3WTcjs.tileCoord.call(void 0, key);
396
- }
397
-
398
- // src/paint/pigment.ts
399
- var PIGMENT_BANDS = 36;
400
- var PIGMENT_LAMBDA_MIN = 380;
401
- var PIGMENT_LAMBDA_STEP = 10;
402
- var PIGMENT_WAVELENGTHS = Float32Array.from({ length: PIGMENT_BANDS }, (_, i) => PIGMENT_LAMBDA_MIN + i * PIGMENT_LAMBDA_STEP);
403
- var N = PIGMENT_BANDS;
404
- var LUT_NODES = 17;
405
- var R_MIN = 1e-4;
406
- var SCATTER_MIN = 0.01;
407
- function toLinear(v) {
408
- return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
409
- }
410
- function toEncoded(v) {
411
- if (!(v > 0)) return 0;
412
- if (v >= 1) return 1;
413
- return v <= 31308e-7 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
414
- }
415
- function clamp01(v) {
416
- return v > 0 ? v < 1 ? v : 1 : 0;
412
+ return _chunkCRMAXIUVcjs.tileCoord.call(void 0, key);
413
+ }
414
+
415
+ // src/paint/mixer.ts
416
+ var MIXING_MODELS = ["rgb", "pigment"];
417
+ var DEFAULT_MIXER = {
418
+ wet: 0.5,
419
+ load: 0.5,
420
+ mix: 0.5,
421
+ loadEachStroke: true,
422
+ cleanEachStroke: true,
423
+ mixing: "rgb"
424
+ };
425
+ var MIXER_PICKUP_RATE = 0.25;
426
+ var MIXER_DRAIN = 0.08;
427
+ function normalizeMixer(raw = {}) {
428
+ const unit = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d;
429
+ const bool = (v, d) => typeof v === "boolean" ? v : d;
430
+ return {
431
+ wet: unit(raw.wet, DEFAULT_MIXER.wet),
432
+ load: unit(raw.load, DEFAULT_MIXER.load),
433
+ mix: unit(raw.mix, DEFAULT_MIXER.mix),
434
+ loadEachStroke: bool(raw.loadEachStroke, DEFAULT_MIXER.loadEachStroke),
435
+ cleanEachStroke: bool(raw.cleanEachStroke, DEFAULT_MIXER.cleanEachStroke),
436
+ mixing: raw.mixing === "pigment" ? "pigment" : "rgb"
437
+ };
417
438
  }
418
- function lobe(l, mu, s1, s2) {
419
- const t = (l - mu) / (l < mu ? s1 : s2);
420
- return Math.exp(-0.5 * t * t);
439
+ var EMPTY_MIXER_LOAD = { reservoir: null, amount: 0, pickup: null };
440
+ function loadMixer(load, color) {
441
+ const c = (v) => Math.min(1, Math.max(0, Number.isFinite(v) ? v : 0));
442
+ return { reservoir: [c(color[0]), c(color[1]), c(color[2])], amount: 1, pickup: load.pickup };
421
443
  }
422
- function planck(nm, kelvin) {
423
- const m = nm * 1e-9;
424
- return 1 / (m ** 5 * (Math.exp(0.01438776877 / (m * kelvin)) - 1));
444
+ function cleanMixer(load) {
445
+ return { reservoir: load.reservoir, amount: load.amount, pickup: null };
425
446
  }
426
- var weights = null;
427
- function rgbWeights() {
428
- if (weights) return weights;
429
- const w = new Float64Array(3 * N);
430
- const m = [3.2404542, -1.5371385, -0.4985314, -0.969266, 1.8760108, 0.041556, 0.0556434, -0.2040259, 1.0572252];
431
- for (let b = 0; b < N; b++) {
432
- const l = PIGMENT_LAMBDA_MIN + b * PIGMENT_LAMBDA_STEP;
433
- const e = planck(l, 6504);
434
- const x = e * (1.056 * lobe(l, 599.8, 37.9, 31) + 0.362 * lobe(l, 442, 16, 26.7) - 0.065 * lobe(l, 501.1, 20.4, 26.2));
435
- const y = e * (0.821 * lobe(l, 568.8, 46.9, 40.5) + 0.286 * lobe(l, 530.9, 16.3, 31.1));
436
- const z = e * (1.217 * lobe(l, 437, 11.8, 36) + 0.681 * lobe(l, 459, 26, 13.8));
437
- for (let c = 0; c < 3; c++) w[c * N + b] = m[c * 3] * x + m[c * 3 + 1] * y + m[c * 3 + 2] * z;
447
+ var MixerStroke = (_class4 = class {
448
+ constructor(brush, settings, load, target) {;_class4.prototype.__init11.call(this);_class4.prototype.__init12.call(this);_class4.prototype.__init13.call(this);_class4.prototype.__init14.call(this);_class4.prototype.__init15.call(this);_class4.prototype.__init16.call(this);_class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);
449
+ this.brush = brush;
450
+ this.target = target;
451
+ this.settings = normalizeMixer(settings);
452
+ this.walker = new DabWalker(brush);
453
+ this.reservoir = load.reservoir ? [load.reservoir[0], load.reservoir[1], load.reservoir[2]] : null;
454
+ this.amount = this.reservoir ? Math.min(1, Math.max(0, load.amount)) : 0;
455
+ this.pickup = load.pickup ? [load.pickup[0], load.pickup[1], load.pickup[2]] : null;
456
+ this.pig = this.settings.mixing === "pigment" ? { res: _chunkCRMAXIUVcjs.createPigment.call(void 0, _nullishCoalesce(this.reservoir, () => ( [0, 0, 0]))), pick: _chunkCRMAXIUVcjs.createPigment.call(void 0, _nullishCoalesce(this.pickup, () => ( [0, 0, 0]))), paint: _chunkCRMAXIUVcjs.createPigment.call(void 0, [0, 0, 0]), canvas: _chunkCRMAXIUVcjs.createPigment.call(void 0, [0, 0, 0]) } : null;
438
457
  }
439
- for (let c = 0; c < 3; c++) {
440
- let sum = 0;
441
- for (let b = 0; b < N; b++) sum += w[c * N + b];
442
- for (let b = 0; b < N; b++) w[c * N + b] /= sum;
458
+
459
+
460
+ __init11() {this.dabCount = 0}
461
+ __init12() {this.bounds = null}
462
+
463
+ __init13() {this.smoothPos = null}
464
+ __init14() {this.buf = new Float32Array(0)}
465
+
466
+
467
+
468
+
469
+
470
+ __init15() {this.out = [0, 0, 0]}
471
+ /** The brush load after the dabs so far. */
472
+ get load() {
473
+ return { reservoir: this.reservoir ? [...this.reservoir] : null, amount: this.amount, pickup: this.pickup ? [...this.pickup] : null };
443
474
  }
444
- return weights = w;
445
- }
446
- function fitCoefficients(r, g, b, coeffs) {
447
- const w = rgbWeights();
448
- const target = [r, g, b];
449
- const res = new Float64Array(3);
450
- const jac = new Float64Array(9);
451
- const trial = new Float64Array(3);
452
- let err = residual(coeffs, target, w, res, jac);
453
- for (let it = 0; it < 60 && err > 1e-10; it++) {
454
- const step = solve3(jac, res);
455
- if (!step) break;
456
- let scale = 1;
457
- let next = Infinity;
458
- for (let k = 0; k < 12; k++) {
459
- for (let i = 0; i < 3; i++) trial[i] = coeffs[i] - scale * step[i];
460
- next = residual(trial, target, w, null, null);
461
- if (next < err) break;
462
- scale *= 0.5;
463
- }
464
- if (!(next < err)) break;
465
- coeffs.set(trial);
466
- err = residual(coeffs, target, w, res, jac);
475
+ push(points) {
476
+ const dabs = [];
477
+ for (const p of points) this.walker.push(this.smooth(p), dabs);
478
+ return this.paint(dabs);
467
479
  }
468
- return err;
469
- }
470
- function residual(c, target, w, res, jac) {
471
- let r0 = -target[0];
472
- let r1 = -target[1];
473
- let r2 = -target[2];
474
- if (jac) jac.fill(0);
475
- for (let b = 0; b < N; b++) {
476
- const x = b / (N - 1);
477
- const z = (c[0] * x + c[1]) * x + c[2];
478
- const q = 1 + z * z;
479
- const sq = Math.sqrt(q);
480
- const s = 0.5 + z / (2 * sq);
481
- r0 += w[b] * s;
482
- r1 += w[N + b] * s;
483
- r2 += w[2 * N + b] * s;
484
- if (jac) {
485
- const ds = 1 / (2 * q * sq);
486
- for (let k = 0; k < 3; k++) {
487
- const wd = w[k * N + b] * ds;
488
- jac[k * 3] += wd * x * x;
489
- jac[k * 3 + 1] += wd * x;
490
- jac[k * 3 + 2] += wd;
491
- }
492
- }
480
+ finish(last) {
481
+ const dabs = [];
482
+ if (last && this.smoothPos && (this.smoothPos.x !== last.x || this.smoothPos.y !== last.y)) this.walker.push(last, dabs);
483
+ this.walker.finish(dabs);
484
+ return this.paint(dabs);
493
485
  }
494
- if (res) {
495
- res[0] = r0;
496
- res[1] = r1;
497
- res[2] = r2;
486
+ smooth(p) {
487
+ const a = 1 - Math.min(0.95, Math.max(0, this.brush.smoothing));
488
+ const cur = this.smoothPos;
489
+ const n = cur && a < 1 ? { ...p, x: cur.x + (p.x - cur.x) * a, y: cur.y + (p.y - cur.y) * a } : p;
490
+ this.smoothPos = n;
491
+ return n;
498
492
  }
499
- return r0 * r0 + r1 * r1 + r2 * r2;
500
- }
501
- function solve3(a, v) {
502
- const [a0, a1, a2, a3, a4, a5, a6, a7, a8] = a;
503
- const c0 = a4 * a8 - a5 * a7;
504
- const c1 = a5 * a6 - a3 * a8;
505
- const c2 = a3 * a7 - a4 * a6;
506
- const det = a0 * c0 + a1 * c1 + a2 * c2;
507
- if (!(Math.abs(det) > 1e-300)) return null;
508
- const [v0, v1, v2] = v;
509
- return [
510
- (v0 * c0 + a1 * (a5 * v2 - v1 * a8) + a2 * (v1 * a7 - a4 * v2)) / det,
511
- (a0 * (v1 * a8 - a5 * v2) + v0 * c1 + a2 * (a3 * v2 - v1 * a6)) / det,
512
- (a0 * (a4 * v2 - v1 * a7) + a1 * (v1 * a6 - a3 * v2) + v0 * c2) / det
513
- ];
514
- }
515
- var candidate = new Float64Array(3);
516
- function fitNode(r, g, b, coeffs) {
517
- let best = fitCoefficients(r, g, b, coeffs);
518
- if (best <= 1e-12) return;
519
- candidate.fill(0);
520
- let err = fitCoefficients(r, g, b, candidate);
521
- if (err < best) {
522
- best = err;
523
- coeffs.set(candidate);
493
+ /** `a` mixed `t` of the way to `b` into `this.out`, by the mixing model. */
494
+ mix(a, pa, b, pb, t) {
495
+ const o = this.out;
496
+ if (pa && pb) _chunkCRMAXIUVcjs.lerpPigmentInto.call(void 0, pa, pb, t, o, 0);
497
+ else for (let c = 0; c < 3; c++) o[c] = a[c] + (b[c] - a[c]) * t;
498
+ return o;
524
499
  }
525
- if (best <= 1e-12) return;
526
- const max = Math.max(r, g, b);
527
- if (!(max > 0) || max >= 0.5) return;
528
- candidate.fill(0);
529
- const steps = 32;
530
- for (let i = 0; i <= steps; i++) {
531
- const scale = 0.5 * Math.pow(max / 0.5, i / steps) / max;
532
- err = fitCoefficients(r * scale, g * scale, b * scale, candidate);
500
+ paint(dabs) {
501
+ const rects = [];
502
+ for (const d of dabs) {
503
+ const rect = dabRect(d, this.brush.hardness);
504
+ const n = rect.width * rect.height;
505
+ if (this.buf.length < n) this.buf = new Float32Array(n);
506
+ const cov = this.buf.subarray(0, n);
507
+ rasterizeDab({ ...d, alpha: 1 }, this.brush.hardness, rect, cov);
508
+ this.dab(d, rect, cov);
509
+ rects.push(rect);
510
+ this.bounds = union(this.bounds, rect);
511
+ this.dabCount++;
512
+ }
513
+ return rects;
533
514
  }
534
- if (err < best) coeffs.set(candidate);
535
- }
536
- function sigmoidSpectrum(c, out, offset) {
537
- for (let b = 0; b < N; b++) {
538
- const x = b / (N - 1);
539
- const z = (c[0] * x + c[1]) * x + c[2];
540
- out[offset + b] = 0.5 + z / (2 * Math.sqrt(1 + z * z));
515
+ // The tile last looked up by `locate` (and whether it came from `writeTile`).
516
+ __init16() {this.tx = NaN}
517
+ __init17() {this.ty = NaN}
518
+ __init18() {this.tile = null}
519
+ __init19() {this.writable = false}
520
+ /** Index of document pixel (dx, dy) in `this.tile`, or -1 when reading an empty tile. */
521
+ locate(dx, dy, write) {
522
+ const t = this.target;
523
+ const lx = dx - t.offset.x;
524
+ const ly = dy - t.offset.y;
525
+ const tx = Math.floor(lx / _chunkCRMAXIUVcjs.TILE);
526
+ const ty = Math.floor(ly / _chunkCRMAXIUVcjs.TILE);
527
+ if (tx !== this.tx || ty !== this.ty || write && !this.writable) {
528
+ this.tx = tx;
529
+ this.ty = ty;
530
+ this.tile = write ? t.writeTile(tx, ty) : t.readTile(tx, ty);
531
+ this.writable = write;
532
+ }
533
+ return this.tile ? ((ly - ty * _chunkCRMAXIUVcjs.TILE) * _chunkCRMAXIUVcjs.TILE + (lx - tx * _chunkCRMAXIUVcjs.TILE)) * 4 : -1;
541
534
  }
542
- }
543
- var NODES = Float64Array.from({ length: LUT_NODES }, (_, i) => toLinear(i / (LUT_NODES - 1)));
544
- var table = null;
545
- function spectrumTable() {
546
- if (table) return table;
547
- const n = LUT_NODES;
548
- const t = new Float32Array(n * n * n * N);
549
- const row = new Float64Array(3);
550
- const coeffs = new Float64Array(3);
551
- for (let r = 0; r < n; r++) {
552
- for (let g = 0; g < n; g++) {
553
- coeffs.set(row);
554
- for (let b = 0; b < n; b++) {
555
- fitNode(NODES[r], NODES[g], NODES[b], coeffs);
556
- if (b === 0) row.set(coeffs);
557
- sigmoidSpectrum(coeffs, t, ((r * n + g) * n + b) * N);
535
+ dab(d, rect, cov) {
536
+ const { wet, mix, load } = this.settings;
537
+ const pig = this.pig;
538
+ const w = rect.width;
539
+ this.tx = NaN;
540
+ let ar = 0;
541
+ let ag = 0;
542
+ let ab = 0;
543
+ let wsum = 0;
544
+ let asum = 0;
545
+ for (let yy = 0; yy < rect.height; yy++) {
546
+ for (let xx = 0; xx < w; xx++) {
547
+ const k = cov[yy * w + xx];
548
+ if (k <= 0) continue;
549
+ wsum += k;
550
+ const i = this.locate(rect.x + xx, rect.y + yy, false);
551
+ if (i < 0) continue;
552
+ const px = this.tile;
553
+ const al = px[i + 3] / 255;
554
+ if (al <= 0) continue;
555
+ const ka = k * al;
556
+ ar += px[i] / 255 * ka;
557
+ ag += px[i + 1] / 255 * ka;
558
+ ab += px[i + 2] / 255 * ka;
559
+ asum += ka;
558
560
  }
559
561
  }
562
+ if (asum > 1e-6 && wet > 0) {
563
+ const canvas = [ar / asum, ag / asum, ab / asum];
564
+ if (!this.pickup) this.pickup = canvas;
565
+ else {
566
+ if (pig) _chunkCRMAXIUVcjs.setPigmentColor.call(void 0, pig.canvas, canvas[0], canvas[1], canvas[2]);
567
+ const o = this.mix(this.pickup, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _ => _.pick]), () => ( null)), canvas, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _2 => _2.canvas]), () => ( null)), MIXER_PICKUP_RATE * wet * (asum / Math.max(1e-6, wsum)));
568
+ this.pickup = [o[0], o[1], o[2]];
569
+ }
570
+ if (pig) _chunkCRMAXIUVcjs.setPigmentColor.call(void 0, pig.pick, this.pickup[0], this.pickup[1], this.pickup[2]);
571
+ }
572
+ const res = this.reservoir;
573
+ const wr = res ? this.amount * (1 - mix * wet) : 0;
574
+ const wp = this.pickup ? mix * wet : 0;
575
+ const total = wr + wp;
576
+ if (total > 1e-6) {
577
+ const paint = res && this.pickup ? [...this.mix(res, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _3 => _3.res]), () => ( null)), this.pickup, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _4 => _4.pick]), () => ( null)), wp / total)] : [..._nullishCoalesce(res, () => ( this.pickup))];
578
+ if (pig) _chunkCRMAXIUVcjs.setPigmentColor.call(void 0, pig.paint, paint[0], paint[1], paint[2]);
579
+ const dep = Math.min(1, total) * this.brush.flow * this.brush.opacity * d.opacity;
580
+ if (dep > 0) this.deposit(rect, cov, paint, dep);
581
+ }
582
+ this.amount *= 1 - MIXER_DRAIN * (1 - load);
560
583
  }
561
- return table = t;
562
- }
563
- var cell = new Int32Array(3);
564
- var frac = new Float64Array(3);
565
- var linear = new Float64Array(3);
566
- function locate(channel, encoded) {
567
- const v = clamp01(encoded);
568
- const i = Math.min(LUT_NODES - 2, Math.floor(v * (LUT_NODES - 1)));
569
- const lo = NODES[i];
570
- const lin = toLinear(v);
571
- const f = (lin - lo) / (NODES[i + 1] - lo);
572
- cell[channel] = i;
573
- frac[channel] = f < 0 ? 0 : f > 1 ? 1 : f;
574
- linear[channel] = lin;
575
- }
576
- function rgbToReflectance(r, g, b, out = new Float32Array(N)) {
577
- const t = spectrumTable();
578
- const n = LUT_NODES;
579
- locate(0, r);
580
- locate(1, g);
581
- locate(2, b);
582
- const [ri, gi, bi] = cell;
583
- const [rf, gf, bf] = frac;
584
- out.fill(0);
585
- for (let corner = 0; corner < 8; corner++) {
586
- const dr = corner >> 2;
587
- const dg = corner >> 1 & 1;
588
- const db = corner & 1;
589
- const weight = (dr ? rf : 1 - rf) * (dg ? gf : 1 - gf) * (db ? bf : 1 - bf);
590
- if (weight === 0) continue;
591
- const base = (((ri + dr) * n + gi + dg) * n + bi + db) * N;
592
- for (let k = 0; k < N; k++) out[k] += weight * t[base + k];
593
- }
594
- return out;
595
- }
596
- function reflectanceToRgb(reflectance, out = [0, 0, 0]) {
597
- const w = rgbWeights();
598
- let r = 0;
599
- let g = 0;
600
- let b = 0;
601
- for (let k = 0; k < N; k++) {
602
- const v = reflectance[k];
603
- r += w[k] * v;
604
- g += w[N + k] * v;
605
- b += w[2 * N + k] * v;
606
- }
607
- out[0] = toEncoded(r);
608
- out[1] = toEncoded(g);
609
- out[2] = toEncoded(b);
610
- return out;
611
- }
612
- var spectrumScratch = new Float32Array(N);
613
- function createPigment(color, options = {}) {
614
- const pigment = { rgb: [0, 0, 0], strength: 1, ks: new Float32Array(N), scatter: 1 };
615
- return setPigmentColor(pigment, color[0], color[1], color[2], options.strength);
616
- }
617
- function setPigmentColor(pigment, r, g, b, strength = pigment.strength) {
618
- const R = rgbToReflectance(r, g, b, spectrumScratch);
619
- const ks = pigment.ks;
620
- for (let k = 0; k < N; k++) {
621
- const v = R[k] > R_MIN ? R[k] : R_MIN;
622
- ks[k] = (1 - v) * (1 - v) / (2 * v);
623
- }
624
- pigment.rgb[0] = clamp01(r);
625
- pigment.rgb[1] = clamp01(g);
626
- pigment.rgb[2] = clamp01(b);
627
- pigment.strength = Number.isFinite(strength) && strength > 0 ? strength : 1;
628
- const y = 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
629
- pigment.scatter = y > SCATTER_MIN ? y : SCATTER_MIN;
630
- return pigment;
631
- }
632
- function ksToRgb(ks, out, offset) {
633
- const w = rgbWeights();
634
- let r = 0;
635
- let g = 0;
636
- let b = 0;
637
- for (let k = 0; k < N; k++) {
638
- const q = ks[k];
639
- const v = 1 + q - Math.sqrt(q * q + 2 * q);
640
- r += w[k] * v;
641
- g += w[N + k] * v;
642
- b += w[2 * N + k] * v;
643
- }
644
- out[offset] = toEncoded(r);
645
- out[offset + 1] = toEncoded(g);
646
- out[offset + 2] = toEncoded(b);
647
- }
648
- var mixScratch = new Float64Array(N);
649
- function mixPigments(colors, weights2) {
650
- if (colors.length === 0) throw new Error("mixPigments needs at least one colour.");
651
- if (weights2.length !== colors.length) throw new Error(`mixPigments got ${colors.length} colours but ${weights2.length} weights; pass one weight per colour.`);
652
- const pigments = colors.map((c) => "ks" in c ? c : createPigment(c));
653
- let total = 0;
654
- for (let i = 0; i < pigments.length; i++) {
655
- const w = weights2[i];
656
- if (!(Number.isFinite(w) && w >= 0)) throw new Error(`Weight ${i} is ${w}; weights must be finite and \u2265 0.`);
657
- total += w * pigments[i].strength * pigments[i].scatter;
658
- }
659
- if (!(total > 0)) throw new Error("mixPigments needs at least one positive weight.");
660
- mixScratch.fill(0);
661
- for (let i = 0; i < pigments.length; i++) {
662
- const p = pigments[i];
663
- const c = weights2[i] * p.strength * p.scatter / total;
664
- if (c === 0) continue;
665
- for (let k = 0; k < N; k++) mixScratch[k] += c * p.ks[k];
584
+ /** Lays `paint` over the pixels under the dab at `dep` × coverage (× selection). */
585
+ deposit(rect, cov, paint, dep) {
586
+ const t = this.target;
587
+ const sel = t.selection;
588
+ const pig = this.pig;
589
+ const w = rect.width;
590
+ const o = this.out;
591
+ let lastKey = -1;
592
+ for (let yy = 0; yy < rect.height; yy++) {
593
+ const dy = rect.y + yy;
594
+ for (let xx = 0; xx < w; xx++) {
595
+ let k = cov[yy * w + xx] * dep;
596
+ if (k <= 0) continue;
597
+ const dx = rect.x + xx;
598
+ if (sel) {
599
+ if (dx < 0 || dy < 0 || dx >= sel.width || dy >= sel.height) continue;
600
+ k *= sel.data[dy * sel.width + dx] / 255;
601
+ if (k <= 0) continue;
602
+ }
603
+ if (k > 1) k = 1;
604
+ if (t.lockTransparency) {
605
+ const ri = this.locate(dx, dy, false);
606
+ if (ri < 0 || this.tile[ri + 3] === 0) continue;
607
+ }
608
+ const i = this.locate(dx, dy, true);
609
+ const px = this.tile;
610
+ const a = px[i + 3] / 255;
611
+ const na = t.lockTransparency ? a : a + (1 - a) * k;
612
+ const share = t.lockTransparency ? k : na > 0 ? k / na : 1;
613
+ if (a <= 0 || share >= 1) {
614
+ o[0] = paint[0];
615
+ o[1] = paint[1];
616
+ o[2] = paint[2];
617
+ } else if (pig) {
618
+ const key = px[i] << 16 | px[i + 1] << 8 | px[i + 2];
619
+ if (key !== lastKey) {
620
+ _chunkCRMAXIUVcjs.setPigmentColor.call(void 0, pig.canvas, px[i] / 255, px[i + 1] / 255, px[i + 2] / 255);
621
+ lastKey = key;
622
+ }
623
+ _chunkCRMAXIUVcjs.lerpPigmentInto.call(void 0, pig.canvas, pig.paint, share, o, 0);
624
+ } else {
625
+ for (let c = 0; c < 3; c++) o[c] = px[i + c] / 255 + (paint[c] - px[i + c] / 255) * share;
626
+ }
627
+ px[i] = Math.round(o[0] * 255);
628
+ px[i + 1] = Math.round(o[1] * 255);
629
+ px[i + 2] = Math.round(o[2] * 255);
630
+ px[i + 3] = Math.round(na * 255);
631
+ }
632
+ }
666
633
  }
667
- const out = [0, 0, 0];
668
- ksToRgb(mixScratch, out, 0);
669
- return out;
670
- }
671
- function lerpPigment(a, b, t) {
672
- const u = clamp01(t);
673
- return mixPigments([a, b], [1 - u, u]);
674
- }
675
- function lerpPigmentInto(a, b, t, out, offset = 0) {
676
- const u = t > 0 ? t < 1 ? t : 1 : 0;
677
- const wa = (1 - u) * a.strength * a.scatter;
678
- const wb = u * b.strength * b.scatter;
679
- const ca = wa / (wa + wb);
680
- const cb = 1 - ca;
681
- const ka = a.ks;
682
- const kb = b.ks;
683
- for (let k = 0; k < N; k++) mixScratch[k] = ca * ka[k] + cb * kb[k];
684
- ksToRgb(mixScratch, out, offset);
634
+ }, _class4);
635
+ function union(a, b) {
636
+ if (!a) return { ...b };
637
+ const x = Math.min(a.x, b.x);
638
+ const y = Math.min(a.y, b.y);
639
+ return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
685
640
  }
686
641
 
687
642
  // src/paint/pigment-tools.ts
@@ -734,8 +689,8 @@ function createPigmentTools() {
734
689
  const rgbs = items.map((item, i) => parseHex(item.color, i));
735
690
  const total = items.reduce((sum, item) => sum + item.weight, 0);
736
691
  if (!(total > 0)) throw new Error("Every weight is 0; give at least one colour a weight above 0.");
737
- const pigments = rgbs.map((rgb, i) => createPigment(rgb, { strength: items[i].strength }));
738
- const mixed = mixPigments(pigments, items.map((item) => item.weight));
692
+ const pigments = rgbs.map((rgb, i) => _chunkCRMAXIUVcjs.createPigment.call(void 0, rgb, { strength: items[i].strength }));
693
+ const mixed = _chunkCRMAXIUVcjs.mixPigments.call(void 0, pigments, items.map((item) => item.weight));
739
694
  const lerp = [0, 1, 2].map((c) => rgbs.reduce((sum, rgb, i) => sum + rgb[c] * items[i].weight, 0) / total);
740
695
  return {
741
696
  mixed: toHex(mixed),
@@ -775,7 +730,5 @@ function createPigmentTools() {
775
730
 
776
731
 
777
732
 
778
-
779
-
780
- exports.DEFAULT_BRUSH = DEFAULT_BRUSH; exports.MAX_BRUSH_SIZE = MAX_BRUSH_SIZE; exports.normalizeBrush = normalizeBrush; exports.tipShape = tipShape; exports.tipFalloff = tipFalloff; exports.tipReach = tipReach; exports.dabCoverage = dabCoverage; exports.smoothPoints = smoothPoints; exports.DabWalker = DabWalker; exports.walkDabs = walkDabs; exports.dabRect = dabRect; exports.rasterizeDab = rasterizeDab; exports.CoverageMap = CoverageMap; exports.StrokeCoverage = StrokeCoverage; exports.applyStrokeToTile = applyStrokeToTile; exports.coverageTileCoord = coverageTileCoord; exports.PIGMENT_BANDS = PIGMENT_BANDS; exports.PIGMENT_LAMBDA_MIN = PIGMENT_LAMBDA_MIN; exports.PIGMENT_LAMBDA_STEP = PIGMENT_LAMBDA_STEP; exports.PIGMENT_WAVELENGTHS = PIGMENT_WAVELENGTHS; exports.rgbToReflectance = rgbToReflectance; exports.reflectanceToRgb = reflectanceToRgb; exports.createPigment = createPigment; exports.setPigmentColor = setPigmentColor; exports.mixPigments = mixPigments; exports.lerpPigment = lerpPigment; exports.lerpPigmentInto = lerpPigmentInto; exports.createPigmentTools = createPigmentTools;
781
- //# sourceMappingURL=chunk-4F3YDIWW.cjs.map
733
+ exports.DEFAULT_BRUSH = DEFAULT_BRUSH; exports.MAX_BRUSH_SIZE = MAX_BRUSH_SIZE; exports.normalizeBrush = normalizeBrush; exports.tipShape = tipShape; exports.tipFalloff = tipFalloff; exports.tipReach = tipReach; exports.dabCoverage = dabCoverage; exports.smoothPoints = smoothPoints; exports.DabWalker = DabWalker; exports.walkDabs = walkDabs; exports.dabRect = dabRect; exports.rasterizeDab = rasterizeDab; exports.CoverageMap = CoverageMap; exports.StrokeCoverage = StrokeCoverage; exports.applyStrokeToTile = applyStrokeToTile; exports.coverageTileCoord = coverageTileCoord; exports.MIXING_MODELS = MIXING_MODELS; exports.DEFAULT_MIXER = DEFAULT_MIXER; exports.MIXER_PICKUP_RATE = MIXER_PICKUP_RATE; exports.MIXER_DRAIN = MIXER_DRAIN; exports.normalizeMixer = normalizeMixer; exports.EMPTY_MIXER_LOAD = EMPTY_MIXER_LOAD; exports.loadMixer = loadMixer; exports.cleanMixer = cleanMixer; exports.MixerStroke = MixerStroke; exports.createPigmentTools = createPigmentTools;
734
+ //# sourceMappingURL=chunk-ZZOTHJ4K.cjs.map