@pixerate/editor 0.93.0 → 0.95.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/THIRD_PARTY_NOTICES.md +3 -0
- package/dist/{chunk-ZOOEX3WT.cjs → chunk-CRMAXIUV.cjs} +369 -7
- package/dist/chunk-CRMAXIUV.cjs.map +1 -0
- package/dist/chunk-E2ILRYTJ.js +3695 -0
- package/dist/chunk-E2ILRYTJ.js.map +1 -0
- package/dist/{chunk-D4ZCU72J.js → chunk-HEW6KDNI.js} +366 -4
- package/dist/chunk-HEW6KDNI.js.map +1 -0
- package/dist/{chunk-ISDCJHTO.js → chunk-IB53LVGV.js} +238 -285
- package/dist/chunk-IB53LVGV.js.map +1 -0
- package/dist/chunk-T6H3JDGA.cjs +3695 -0
- package/dist/chunk-T6H3JDGA.cjs.map +1 -0
- package/dist/{chunk-4F3YDIWW.cjs → chunk-ZZOTHJ4K.cjs} +264 -311
- package/dist/chunk-ZZOTHJ4K.cjs.map +1 -0
- package/dist/image-editor/index.cjs +1252 -588
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +164 -34
- package/dist/image-editor/index.d.ts +164 -34
- package/dist/image-editor/index.js +913 -249
- package/dist/image-editor/index.js.map +1 -1
- package/dist/paint/index.cjs +22 -3
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +117 -2
- package/dist/paint/index.d.ts +117 -2
- package/dist/paint/index.js +32 -13
- package/dist/raster/index.cjs +83 -3
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.d.cts +99 -348
- package/dist/raster/index.d.ts +99 -348
- package/dist/raster/index.js +83 -3
- package/dist/{surface-fJhkmNQn.d.cts → surface-CgAiXVI3.d.cts} +23 -11
- package/dist/{surface-fJhkmNQn.d.ts → surface-CgAiXVI3.d.ts} +23 -11
- package/dist/zlib-B3LIoxF-.d.cts +796 -0
- package/dist/zlib-eiTEEj1z.d.ts +796 -0
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +82 -7
- package/dist/chunk-4F3YDIWW.cjs.map +0 -1
- package/dist/chunk-D4ZCU72J.js.map +0 -1
- package/dist/chunk-H543GD6T.cjs +0 -1696
- package/dist/chunk-H543GD6T.cjs.map +0 -1
- package/dist/chunk-ISDCJHTO.js.map +0 -1
- package/dist/chunk-P5TPUNBZ.js +0 -1696
- package/dist/chunk-P5TPUNBZ.js.map +0 -1
- package/dist/chunk-ZOOEX3WT.cjs.map +0 -1
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TILE,
|
|
3
3
|
blendPixel,
|
|
4
|
+
createPigment,
|
|
5
|
+
lerpPigmentInto,
|
|
6
|
+
mixPigment8,
|
|
7
|
+
mixPigments,
|
|
8
|
+
setPigmentColor,
|
|
4
9
|
tileCoord,
|
|
5
10
|
tileKey
|
|
6
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-HEW6KDNI.js";
|
|
7
12
|
|
|
8
13
|
// src/paint/brush.ts
|
|
9
14
|
var DEFAULT_BRUSH = {
|
|
@@ -381,6 +386,18 @@ 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
|
+
mixPigment8(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
402
|
const o = blendPixel(s.blend, cb, src, k);
|
|
386
403
|
out[i] = Math.round(o[0] * 255);
|
|
@@ -395,293 +412,231 @@ function coverageTileCoord(key) {
|
|
|
395
412
|
return tileCoord(key);
|
|
396
413
|
}
|
|
397
414
|
|
|
398
|
-
// src/paint/
|
|
399
|
-
var
|
|
400
|
-
var
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
return
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
|
423
|
-
|
|
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
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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 = class {
|
|
448
|
+
constructor(brush, settings, load, target) {
|
|
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: createPigment(this.reservoir ?? [0, 0, 0]), pick: createPigment(this.pickup ?? [0, 0, 0]), paint: createPigment([0, 0, 0]), canvas: createPigment([0, 0, 0]) } : null;
|
|
438
457
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
458
|
+
brush;
|
|
459
|
+
target;
|
|
460
|
+
dabCount = 0;
|
|
461
|
+
bounds = null;
|
|
462
|
+
walker;
|
|
463
|
+
smoothPos = null;
|
|
464
|
+
buf = new Float32Array(0);
|
|
465
|
+
settings;
|
|
466
|
+
reservoir;
|
|
467
|
+
amount;
|
|
468
|
+
pickup;
|
|
469
|
+
pig;
|
|
470
|
+
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
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) lerpPigmentInto(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
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
515
|
+
// The tile last looked up by `locate` (and whether it came from `writeTile`).
|
|
516
|
+
tx = NaN;
|
|
517
|
+
ty = NaN;
|
|
518
|
+
tile = null;
|
|
519
|
+
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 / TILE);
|
|
526
|
+
const ty = Math.floor(ly / 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 * TILE) * TILE + (lx - tx * TILE)) * 4 : -1;
|
|
541
534
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
for (let
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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) setPigmentColor(pig.canvas, canvas[0], canvas[1], canvas[2]);
|
|
567
|
+
const o = this.mix(this.pickup, pig?.pick ?? null, canvas, pig?.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) setPigmentColor(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, pig?.res ?? null, this.pickup, pig?.pick ?? null, wp / total)] : [...res ?? this.pickup];
|
|
578
|
+
if (pig) setPigmentColor(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
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
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
|
+
setPigmentColor(pig.canvas, px[i] / 255, px[i + 1] / 255, px[i + 2] / 255);
|
|
621
|
+
lastKey = key;
|
|
622
|
+
}
|
|
623
|
+
lerpPigmentInto(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
|
-
|
|
668
|
-
|
|
669
|
-
return
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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
|
+
};
|
|
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
|
|
@@ -765,17 +720,15 @@ export {
|
|
|
765
720
|
StrokeCoverage,
|
|
766
721
|
applyStrokeToTile,
|
|
767
722
|
coverageTileCoord,
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
lerpPigment,
|
|
778
|
-
lerpPigmentInto,
|
|
723
|
+
MIXING_MODELS,
|
|
724
|
+
DEFAULT_MIXER,
|
|
725
|
+
MIXER_PICKUP_RATE,
|
|
726
|
+
MIXER_DRAIN,
|
|
727
|
+
normalizeMixer,
|
|
728
|
+
EMPTY_MIXER_LOAD,
|
|
729
|
+
loadMixer,
|
|
730
|
+
cleanMixer,
|
|
731
|
+
MixerStroke,
|
|
779
732
|
createPigmentTools
|
|
780
733
|
};
|
|
781
|
-
//# sourceMappingURL=chunk-
|
|
734
|
+
//# sourceMappingURL=chunk-IB53LVGV.js.map
|